aboutsummaryrefslogtreecommitdiff
path: root/lib/PacketManager/packets
diff options
context:
space:
mode:
Diffstat (limited to 'lib/PacketManager/packets')
-rw-r--r--lib/PacketManager/packets/FSNETCMD_ADDOBJECT.py65
-rw-r--r--lib/PacketManager/packets/FSNETCMD_AIRCMD.py53
-rw-r--r--lib/PacketManager/packets/FSNETCMD_AIRPLANESTATE.py250
-rw-r--r--lib/PacketManager/packets/FSNETCMD_EMPTYPACKET.py19
-rw-r--r--lib/PacketManager/packets/FSNETCMD_ENVIRONMENT.py51
-rw-r--r--lib/PacketManager/packets/FSNETCMD_ERROR.py22
-rw-r--r--lib/PacketManager/packets/FSNETCMD_GETDAMAGE.py43
-rw-r--r--lib/PacketManager/packets/FSNETCMD_JOINAPPROVAL.py22
-rw-r--r--lib/PacketManager/packets/FSNETCMD_JOINREQUEST.py29
-rw-r--r--lib/PacketManager/packets/FSNETCMD_KILLSERVER.py14
-rw-r--r--lib/PacketManager/packets/FSNETCMD_LOADFIELD.py28
-rw-r--r--lib/PacketManager/packets/FSNETCMD_LOCKON.py31
-rw-r--r--lib/PacketManager/packets/FSNETCMD_LOGOFF.py19
-rw-r--r--lib/PacketManager/packets/FSNETCMD_LOGON.py42
-rw-r--r--lib/PacketManager/packets/FSNETCMD_MISSILELAUNCH.py64
-rw-r--r--lib/PacketManager/packets/FSNETCMD_NULL.py16
-rw-r--r--lib/PacketManager/packets/FSNETCMD_PREPARESIMULATION.py14
-rw-r--r--lib/PacketManager/packets/FSNETCMD_READBACK.py39
-rw-r--r--lib/PacketManager/packets/FSNETCMD_REJECTJOINREQ.py21
-rw-r--r--lib/PacketManager/packets/FSNETCMD_REMOVEAIRPLANE.py13
-rw-r--r--lib/PacketManager/packets/FSNETCMD_REMOVEGROUND.py13
-rw-r--r--lib/PacketManager/packets/FSNETCMD_REPORTSCORE.py50
-rw-r--r--lib/PacketManager/packets/FSNETCMD_REQUESTTESTAIRPLANE.py14
-rw-r--r--lib/PacketManager/packets/FSNETCMD_SERVER_FORCE_JOIN.py14
-rw-r--r--lib/PacketManager/packets/FSNETCMD_SMOKECOLOR.py25
-rw-r--r--lib/PacketManager/packets/FSNETCMD_TESTPACKET.py14
-rw-r--r--lib/PacketManager/packets/FSNETCMD_TEXTMESSAGE.py24
-rw-r--r--lib/PacketManager/packets/FSNETCMD_UNJOIN.py26
-rw-r--r--lib/PacketManager/packets/__init__.py472
29 files changed, 1507 insertions, 0 deletions
diff --git a/lib/PacketManager/packets/FSNETCMD_ADDOBJECT.py b/lib/PacketManager/packets/FSNETCMD_ADDOBJECT.py
new file mode 100644
index 0000000..f2e831e
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_ADDOBJECT.py
@@ -0,0 +1,65 @@
+from struct import pack, unpack
+
+class FSNETCMD_ADDOBJECT: #5
+ """
+ This packet is sent by the server to add an object to the client.
+ """
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ self.object_type = None
+ self.net_type = None
+ self.object_id = None
+ self.iff = None
+ self.pos = [0,0,0]
+ self.atti = [0,0,0]
+ self.identifier = None
+ self.substrname = None
+ self.ysfid = None
+ self.flags = None
+ self.flags0 = None
+ self.outsideRadius = None
+ self.aircraft_class = None
+ self.aircraft_category = None
+ self.pilot = None
+
+ if len(buffer)>=120 and should_decode:
+ self.decode()
+
+ def decode(self):
+ self.object_type, self.net_type = unpack("hh", self.buffer[4:8])
+ #If object_type = 0, then it's an aircraft, and client will send
+ # FSNETREADBACK_ADDAIRPLANE
+ #If object_types = 1, then it's a ground object, and client will send
+ # FSNETREADBACK_ADDGROUND
+ self.object_id = unpack("I", self.buffer[8:12])[0]
+ self.iff, _ = unpack("hh", self.buffer[12:16])
+ self.pos = unpack("fff", self.buffer[16:28])
+ self.atti = unpack("fff", self.buffer[28:40])
+ self.identifier = unpack("32s", self.buffer[40:72])[0].decode().strip('\x00')
+ self.substrname = unpack("32s", self.buffer[72:104])[0].decode().strip('\x00')
+ self.ysfid = unpack("I", self.buffer[104:108])[0]
+ self.flags, self.flags0 = unpack("II", self.buffer[108:116])
+ self.outsideRadius = unpack("f", self.buffer[116:120])[0]
+
+ if len(self.buffer)>=128:
+ self.aircraft_class, self.aircraft_category = unpack("hh", self.buffer[120:124])
+ #There is an extra short, but it's just set to 0
+ if len(self.buffer) >= 176:
+ self.pilot = unpack("32s", self.buffer[124:156])[0].decode().strip('\x00')
+
+ @staticmethod
+ def encode(object_type, net_type, object_id, iff, pos, atti, identifier, substrname, ysfid,
+ flags, flags0, outside_radius, aircraft_class=None, aircraft_category=None,
+ pilot=None, with_size:bool=False):
+
+ buffer = pack("IHHIHHfff32s32sIIIf", 5, object_type, net_type, object_id,
+ iff, 0, pos[0], pos[1], pos[2], atti[0], atti[1], atti[2],
+ identifier.encode(), substrname.encode(), ysfid, flags,
+ flags0, outside_radius)
+ if aircraft_class and aircraft_category:
+ buffer += pack("hhh", aircraft_class, aircraft_category, 0)
+ if pilot:
+ buffer += pack("32s", pilot.encode())
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_AIRCMD.py b/lib/PacketManager/packets/FSNETCMD_AIRCMD.py
new file mode 100644
index 0000000..50340c1
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_AIRCMD.py
@@ -0,0 +1,53 @@
+from struct import pack, unpack
+from . import AIRCMD_KEYWORDS
+
+class FSNETCMD_AIRCMD: #30
+
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ self.aircraft_id = None
+ self.message = None
+ self.command = None
+ if should_decode:
+ self.decode()
+
+ def decode(self):
+ self.aircraft_id = unpack("I", self.buffer[4:8])[0]
+ self.message = self.buffer[8:].decode("utf-8").strip("\x00")
+ if self.message.startswith('*'):
+ self.command = self.get_command_from_message(self.message)
+
+ def get_command_from_message(self, message:str):
+ """
+ If the messages are prefixed with a *, then it is usually an engine/.dat
+ command, such as WEIGFUEL being the weight of the fuel.
+ Command would be something like *43 1000
+ """
+ message = message[1:]
+ engine_code = message.split(" ")[0]
+ try:
+ command = int(engine_code)
+ command = AIRCMD_KEYWORDS[command]
+ except ValueError:
+ return None
+ value = message.split(" ")[1]
+ return command, value
+
+ @staticmethod
+ def encode(aircraft_id:int, message:str, with_size:bool=False):
+ buffer = pack("I",30)+pack("I",aircraft_id)+message.encode("utf-8")
+ buffer = buffer + b"\x00" #Last padding
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer
+
+ @staticmethod
+ def setPayload(aircraft_id:int, payload:int, units:str='kg', with_size:bool=False):
+ """
+ This will set the payload of the aircraft, useful if you want to
+ load or unload passengers/cargo - for YSRP I use this to load and unload
+ on mission start and end.
+ """
+ payload = str(payload)
+ message = f"INITLOAD {payload} {units}"
+ return FSNETCMD_AIRCMD.encode(aircraft_id, message, with_size)
diff --git a/lib/PacketManager/packets/FSNETCMD_AIRPLANESTATE.py b/lib/PacketManager/packets/FSNETCMD_AIRPLANESTATE.py
new file mode 100644
index 0000000..382c876
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_AIRPLANESTATE.py
@@ -0,0 +1,250 @@
+from struct import pack, unpack
+from math import pi
+
+class FSNETCMD_AIRPLANESTATE: #11
+ """
+ This packet is sent by the client to the server to update the state of the aircraft.
+ The server sends this to the client to update the state of the aircraft.
+
+ Versions:
+ Version 0: vh, vp and vr are 16bit shorts
+ >= 1: vh, vp and vr are 32bit integer appended at the end. Because ...why?
+ >=2: includes thrust vector, reverser and bombbay
+ >=3: idOnServer is 32bit int
+ 4 and 5: Short version of vh, vp and vr
+ 4: thrust vector and bombbay
+ 5: no thrust vector or bombbay
+ """
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ self.remote_time = None
+ self.player_id = None
+ self.packet_version = None
+ self.position = [0,0,0]
+ self.atti = [0,0,0]
+ self.velocity = [0,0,0]
+ self.atti_velocity = [0,0,0]
+ self.smoke_oil = None
+ self.fuel = None
+ self.payload = None
+ self.flight_state = None
+ self.vgw = None
+ self.spoiler = None
+ self.landing_gear = None
+ self.flap = None
+ self.brake = None
+ self.flags = {
+ "ab": None,
+ "firing": None,
+ "smoke": None,
+ "nav_lights": False,
+ "beacon": False,
+ "strobe": False,
+ "landing_lights": False,
+ }
+ self.gun_ammo = None
+ self.rocket_ammo = None
+ self.aam = None
+ self.agm = None
+ self.bomb = None
+ self.life = None
+ self.g_value = None
+ self.throttle = None
+ self.elev = None
+ self.ail = None
+ self.rud = None
+ self.trim = None
+ self.thrust_vector = {
+ "vector": None,
+ "reverser": None
+ }
+ self.bomb_bay_info = None
+ if should_decode:
+ self.decode()
+
+ def decode(self):
+ self.remote_time, self.player_id = unpack("fI", self.buffer[4:12])
+ self.packet_version = unpack("H", self.buffer[16:18])[0]
+ if self.packet_version == 4 or self.packet_version == 5:
+
+ self.position = list(unpack("fff", self.buffer[18:30]))
+ self.atti = list(map(lambda x: x / (pi / 32768.0),
+ unpack("hhh", self.buffer[30:36])))
+ self.velocity = list(map(lambda x: x/10,
+ unpack("hhh", self.buffer[36:42])))
+ self.atti_velocity = list(map(lambda x: x / (pi / 32768.0),
+ unpack("hhh", self.buffer[42:48])))
+ self.smoke_oil, self.fuel, self.payload, _ = unpack("hhhh", self.buffer[48:56])
+
+ self.flight_state, self.vgw = unpack("BB", self.buffer[56:58])
+ self.vgw = self.vgw / 255.0
+
+ c = unpack("B", self.buffer[58:59])[0]
+ self.spoiler = (c >> 4 & 15) / 15.0 #Bitshift 4 to the right, then mask with 15
+ self.landing_gear = (c & 15) / 15.0 #Mask with 15
+
+ c = unpack("B", self.buffer[59:60])[0]
+ self.flap = (c >> 4 & 15) / 15.0
+ self.brake = (c & 15) / 15.0
+
+ flags = unpack("h", self.buffer[60:62])[0]
+ self.flags["ab"] = bool(flags & 1) # Last bit
+ self.flags["firing"] = bool(flags &8)
+ self.flags["smoke"] = 0
+
+ if flags & 2:
+ self.flags["smoke"] = (flags >> 8) & 255 # bitshift 8 to the right,
+ #then mask with 255
+ if self.flags["smoke"] == 0:
+ self.flags["smoke"] = 255 # Need to review what this actuall does!
+ if flags & 16:
+ self.flags["beacon"] = True
+ if flags & 32:
+ self.flags["nav_lights"] = True
+ if flags & 64:
+ self.flags["strobe"] = True
+ if flags & 128:
+ self.flags["landing_lights"] = True
+
+ self.gun_ammo, self.rocket_ammo, self.aam, self.agm, self.bomb = unpack("HHBBB", self.buffer[62:68])
+ self.life = unpack("B", self.buffer[68:69])[0]
+
+ self.g_value = unpack("b", self.buffer[69:70])[0]/10.0
+
+ self.throttle = unpack("B", self.buffer[70:71])[0]/99.0
+ self.elev = unpack("b", self.buffer[71:72])[0]/99.0
+ self.ail = unpack("b", self.buffer[72:73])[0]/99.0
+ self.rud = unpack("b", self.buffer[73:74])[0]/99.0
+ self.trim = unpack("b", self.buffer[74:75])[0]/99.0
+
+ if self.packet_version == 4:
+ c = unpack("B", self.buffer[75:76])[0]
+ self.thrust_vector["vector"] = (c >> 4 & 15) / 15.0
+ self.thrust_vector["reverser"] = (c & 15) / 15.0
+ c = unpack("B", self.buffer[76:77])[0]
+ self.bomb_bay_info = (c >> 4 & 15) / 15.0
+
+ else:
+
+ self.position = list(unpack("fff", self.buffer[20:32]))
+ self.atti = list(map(lambda x: x / (pi / 32768.0),
+ unpack("hhh", self.buffer[32:38])))
+
+ self.velocity = list(map(lambda x: x/10,
+ unpack("hhh", self.buffer[38:44])))
+
+ self.atti_velocity = list(map(lambda x: x / (pi / 32768.0),
+ unpack("hhh", self.buffer[44:50])))
+ self.g_value = unpack("h", self.buffer[50:52])[0]/100.0
+
+ self.gun_ammo, self.aam, self.agm, self.bomb, self.smoke_oil = unpack("HHHHH", self.buffer[52:62])
+
+ self.payload = unpack("f", self.buffer[62:66])[0]
+
+ self.life = unpack("H", self.buffer[66:68])[0]
+
+ self.flight_state, self.vgw = unpack("BB", self.buffer[68:70])
+ self.vgw = self.vgw / 255.0
+ self.spoiler = unpack("B", self.buffer[70:71])[0]/255.0
+ self.landing_gear = unpack("B", self.buffer[71:72])[0]/255.0
+ self.flap = unpack("B", self.buffer[72:73])[0]/255.0
+ self.brake = unpack("B", self.buffer[73:74])[0]/255.0
+
+ flags = unpack("H", self.buffer[74:76])[0]
+ self.flags["ab"] = bool(flags & 1)
+ self.flags["firing"] = bool(flags & 8)
+ self.flags["smoke"] = 0
+ if flags & 2:
+ self.flags["smoke"] = (flags >> 8) & 255
+ if self.flags["smoke"] == 0:
+ self.flags["smoke"] = 255
+
+ self.throttle = unpack("B", self.buffer[76:77])[0]/99.0
+ self.elev = unpack("b", self.buffer[77:78])[0]/99.0
+ self.ail = unpack("b", self.buffer[78:79])[0]/99.0
+ self.rud = unpack("b", self.buffer[79:80])[0]/99.0
+ self.trim = unpack("b", self.buffer[80:81])[0]/99.0
+
+ self.rocket_ammo = unpack("H", self.buffer[81:83])[0]
+
+ if self.packet_version >= 1:
+ self.atti_velocity = list(map(lambda x: x / (pi / 32768.0),
+ unpack("fff", self.buffer[83:95])))
+
+ if self.packet_version >= 2:
+ self.thrust_vector["vector"] = unpack("B", self.buffer[95:96])[0]/255.0
+ self.thrust_vector["reverser"] = unpack("B", self.buffer[96:97])[0]/255.0
+ self.bomb_bay_info = unpack("B", self.buffer[97:98])[0]/255.0
+
+ @staticmethod
+ def encode(remote_time, player_id, packet_version, position, atti, velocity, atti_velocity,
+ smoke_oil, fuel, payload, flight_state, vgw, spoiler, landing_gear, flap, brake,
+ flags, gun_ammo, rocket_ammo, aam, agm, bomb, life, g_value, throttle, elev, ail, rud,
+ trim, thrust_vector, bomb_bay_info, with_size:bool=False):
+ buffer = pack("IfI", 11, remote_time, player_id)
+ buffer += pack("H", packet_version)
+ if packet_version == 4 or packet_version == 5:
+ buffer += pack("fffhhhhhhhhh", *position, *atti, *velocity, *atti_velocity)
+ buffer += pack("hhhh", smoke_oil, fuel, payload, 0)
+ buffer += pack("BB", flight_state, int(vgw*255))
+ buffer += pack("BB", int(spoiler*15)<<4 | int(landing_gear*15), int(flap*15)<<4 | int(brake*15))
+ flagschar = 0
+ if flags["ab"]:
+ flagschar |= 1
+ if flags["firing"]:
+ flagschar |= 8
+ if flags["smoke"]:
+ flagschar |= flags["smoke"] << 8
+ if flags["beacon"]:
+ flagschar |= 16
+ if flags["nav_lights"]:
+ flagschar |= 32
+ if flags["strobe"]:
+ flagschar |= 64
+ if flags["landing_lights"]:
+ flagschar |= 128
+ buffer += pack("h", flagschar)
+ buffer += pack("HHBBB", gun_ammo, rocket_ammo, aam, agm, bomb)
+ buffer += pack("B", life)
+ buffer += pack("b", int(g_value*10))
+ buffer += pack("B", int(throttle*99))
+ buffer += pack("b", int(elev*99))
+ buffer += pack("b", int(ail*99))
+ buffer += pack("b", int(rud*99))
+ buffer += pack("b", int(trim*99))
+
+ if packet_version == 4:
+ buffer += pack("BB", int(thrust_vector["vector"]*15)<<4 | int(thrust_vector["reverser"]*15),
+ int(bomb_bay_info*15))
+
+ else:
+ buffer += pack("fffhhhfff", *position, *atti, *velocity, *atti_velocity)
+ buffer += pack("HHHHH", gun_ammo, aam, agm, bomb, smoke_oil)
+ buffer += pack("f", payload)
+ buffer += pack("H", life)
+ buffer += pack("BB", flight_state, int(vgw*255))
+ buffer += pack("BBBB", int(spoiler*15), int(landing_gear*15),
+ int(flap*15), int(brake*15))
+ flagschar = 0
+ if flags["ab"]:
+ flagschar |= 1
+ if flags["firing"]:
+ flagschar |= 8
+ if flags["smoke"]:
+ flagschar |= flags["smoke"] << 8
+ buffer += pack("H", flagschar)
+ buffer += pack("B", int(throttle*99))
+ buffer += pack("b", int(elev*99))
+ buffer += pack("b", int(ail*99))
+ buffer += pack("b", int(rud*99))
+ buffer += pack("b", int(trim*99))
+ buffer += pack("H", rocket_ammo)
+ if packet_version >= 1:
+ buffer += pack("fff", *atti_velocity)
+ if packet_version >= 2:
+ buffer += pack("BBB", int(thrust_vector["vector"]*255), int(thrust_vector["reverser"]*255),
+ int(bomb_bay_info*255))
+
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer
diff --git a/lib/PacketManager/packets/FSNETCMD_EMPTYPACKET.py b/lib/PacketManager/packets/FSNETCMD_EMPTYPACKET.py
new file mode 100644
index 0000000..cfb35d6
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_EMPTYPACKET.py
@@ -0,0 +1,19 @@
+from struct import pack
+
+class FSNETCMD_EMPTYPACKET:
+ """
+ A template function for empty packets"""
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ if should_decode:
+ self.decode()
+
+ def decode(self):
+ pass # There are no messages in this packet!
+
+ @staticmethod
+ def encode(with_size:bool=False): #This will be extended by each func.
+ buffer = pack("I",14)
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_ENVIRONMENT.py b/lib/PacketManager/packets/FSNETCMD_ENVIRONMENT.py
new file mode 100644
index 0000000..4937fba
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_ENVIRONMENT.py
@@ -0,0 +1,51 @@
+from struct import pack, unpack
+
+class FSNETCMD_ENVIRONMENT: #33
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ self.day_night = None # 0 is day, 1 is night
+ self.flags = {
+ "fog":False,
+ "blackout":False,
+ "midair":False,
+ "can_land_anywhere":False
+ }
+ self.wind = [0,0,0]
+ self.visibility = None
+ if should_decode:
+ self.decode()
+
+ def decode(self):
+ variables = unpack("IHHIffff", self.buffer[0:28])
+ #0 is the packet type, 1 is just padding.
+ self.day_night = variables[2]
+ flags = variables[3]
+ self.wind = list(variables[4:7])
+ self.visibility = variables[7]
+ self.flags["fog"] = bool(flags & 1)
+ if flags&8 == 1: #Server controls blackout:
+ self.flags["blackout"] = bool(flags & 4)
+ if flags&32 == 1: #Server controls midair:
+ self.flags["midair"] = bool(flags & 16)
+ if flags&128 == 1: #Server controls can_land_anywhere:
+ self.flags["can_land_anywhere"] = bool(flags & 64)
+
+ @staticmethod
+ def encode(day_night, fog, blackout, midair, can_land_anywhere, wind, visibility, with_size:bool=False):
+ flags = 0
+ if fog:
+ flags |= 1
+ if blackout:
+ flags |= 4
+ flags |=8
+ if midair:
+ flags += 16
+ flags += 32
+ if can_land_anywhere:
+ flags += 64
+ flags += 128
+ buffer = pack("IHHIffff", 33, 0, day_night, flags, *wind, visibility)
+ if with_size:
+ return pack("I", len(buffer))+buffer
+ return buffer
+
diff --git a/lib/PacketManager/packets/FSNETCMD_ERROR.py b/lib/PacketManager/packets/FSNETCMD_ERROR.py
new file mode 100644
index 0000000..874ca51
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_ERROR.py
@@ -0,0 +1,22 @@
+from struct import pack, unpack
+from . import ERROR_CODES
+
+class FSNETCMD_ERROR: #3
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ self.error_code = None
+ self.error_message = None
+ if should_decode:
+ self.decode()
+
+ def decode(self):
+ errorCode = unpack("I",self.buffer[4:8])[0]
+ if errorCode < len(ERROR_CODES):
+ self.error_message = ERROR_CODES[errorCode]
+
+ @staticmethod
+ def encode(error_code, with_size:bool=False):
+ buffer = pack("I",3)+pack("I",error_code)
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer
diff --git a/lib/PacketManager/packets/FSNETCMD_GETDAMAGE.py b/lib/PacketManager/packets/FSNETCMD_GETDAMAGE.py
new file mode 100644
index 0000000..e7988a6
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_GETDAMAGE.py
@@ -0,0 +1,43 @@
+from struct import pack, unpack
+from . import FSWEAPON_DICT
+
+class FSNETCMD_GETDAMAGE: #22
+ """
+ Sent when an aircraft or ground target has taken damage
+ """
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ self.victim_id = None
+ self.victim_type = None
+ self.attacker_type = None
+ self.attacker_id = None
+ self.damage = None
+ self.died_of = None
+ self.weapon_type = None
+ if should_decode:
+ self.decode()
+
+ def decode(self):
+ variables = unpack("IIIIIHHH", self.buffer[0:26])
+ self.victim_id = variables[2]
+ self.victim_type = variables[1]
+ self.attacker_type = variables[3]
+ self.attacker_id = variables[4]
+ self.damage = variables[5]
+ self.died_of = variables[6]
+ self.weapon_type = variables[7]
+ if self.weapon_type in FSWEAPON_DICT:
+ self.weapon_type = FSWEAPON_DICT[self.weapon_type]
+
+
+ @staticmethod
+ def encode(victim_id, victim_type, attacker_type, attacker_id, damage, died_of,
+ weapon_type, with_size:bool=False):
+ if weapon_type in FSWEAPON_DICT and not isinstance(weapon_type,int):
+ weapon_type = list(FSWEAPON_DICT.keys())[list(FSWEAPON_DICT.values()).index(weapon_type)]
+
+ buffer = pack("I",22)+pack("IIIIIHHH", victim_id, victim_type, attacker_type,
+ attacker_id, damage, died_of, weapon_type)
+ if with_size:
+ return pack("I", len(buffer))+buffer
+ return buffer
diff --git a/lib/PacketManager/packets/FSNETCMD_JOINAPPROVAL.py b/lib/PacketManager/packets/FSNETCMD_JOINAPPROVAL.py
new file mode 100644
index 0000000..beb6ab9
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_JOINAPPROVAL.py
@@ -0,0 +1,22 @@
+from struct import pack
+
+class FSNETCMD_JOINAPPROVAL: #9
+ """
+ When the server sends the "add aircraft" command, and
+ receves the readback from the client, they'll send this
+ It's an empty packet, but can be useful to know the client is about to join.
+ """
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ if should_decode:
+ self.decode()
+
+ def decode(self):
+ pass
+
+ @staticmethod
+ def encode(with_size:bool=False):
+ buffer = pack("I",9)
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_JOINREQUEST.py b/lib/PacketManager/packets/FSNETCMD_JOINREQUEST.py
new file mode 100644
index 0000000..0df7287
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_JOINREQUEST.py
@@ -0,0 +1,29 @@
+from struct import pack, unpack
+
+class FSNETCMD_JOINREQUEST: #8
+ """
+ The client sends a join request to the server with their iff, aircraft, start position, fuel and smoke
+ The server replies with the join request readback,The server replies with the join request readback
+ """
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ self.iff = None
+ self.aircraft = None
+ self.start_pos = None #It's the STP name
+ self.fuel = None
+ self.smoke = None
+ if should_decode:
+ self.decode()
+
+ def decode(self):
+ self.iff, _, self.aircraft, self.start_pos, _, self.fuel, self.smoke = unpack("HH32s32sHHH", self.buffer[4:78])
+ self.start_pos = self.start_pos.decode().strip('\x00')
+ self.aircraft = self.aircraft.decode().strip('\x00')
+
+ @staticmethod
+ def encode(iff, aircraft, start_pos, fuel, smoke, with_size:bool=False):
+ buffer = pack("IHH32s32sHHH", 8, iff, 0, aircraft.encode(),
+ start_pos.encode(), 0, fuel, smoke)
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_KILLSERVER.py b/lib/PacketManager/packets/FSNETCMD_KILLSERVER.py
new file mode 100644
index 0000000..3e1b4ec
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_KILLSERVER.py
@@ -0,0 +1,14 @@
+from struct import pack
+from .FSNETCMD_EMPTYPACKET import FSNETCMD_EMPTYPACKET
+
+class FSNETCMD_KILLSERVER(FSNETCMD_EMPTYPACKET): #15
+ """
+ This is unimplemented in YS, but it would shutdown the server
+ The actual server functionality is disabled.
+ """
+ @staticmethod
+ def encode(with_size:bool=False):
+ buffer = pack("I",15)
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_LOADFIELD.py b/lib/PacketManager/packets/FSNETCMD_LOADFIELD.py
new file mode 100644
index 0000000..2994591
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_LOADFIELD.py
@@ -0,0 +1,28 @@
+from struct import pack, unpack
+
+class FSNETCMD_LOADFIELD: #4
+ """
+ This packet is sent by the server along with the field info. When received,
+ the client replies with the same packet.
+ """
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ self.field = None
+ self.fieldShortName = None
+ self.flags = None
+ self.pos = [0,0,0]
+ self.atti = [0,0,0]
+ if len(buffer) == 64 and should_decode:
+ self.decode()
+
+ def decode(self):
+ self.field, self.flags, self.pos[0], self.pos[1], self.pos[2], self.atti[0], self.atti[1], self.atti[2] = unpack("32sIffffff", self.buffer[4:])
+ self.fieldShortName = self.field.split(b'\x00')[0].decode()
+
+ @staticmethod
+ def encode(field, flags, pos, atti, with_size:bool=False):
+ buffer = pack("I32sIffffff", 4, field.encode(), flags, pos[0], pos[1],
+ pos[2], atti[0], atti[1], atti[2])
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_LOCKON.py b/lib/PacketManager/packets/FSNETCMD_LOCKON.py
new file mode 100644
index 0000000..2b3d01a
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_LOCKON.py
@@ -0,0 +1,31 @@
+from struct import pack, unpack
+
+class FSNETCMD_LOCKON: #18
+ """
+ Sent from server to client, and client to server
+ when someone locks onto someone else
+ """
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ self.locker_id = None
+ self.locker_is_air = False
+ self.lockee_id = None
+ self.lockee_is_air = False
+ if should_decode:
+ self.decode()
+
+ def decode(self):
+ self.locker_id, self.locker_is_air, self.lockee_id, self.lockee_is_air = unpack("IIII", self.buffer[4:20])
+ self.locker_is_air = bool(self.locker_is_air)
+ self.lockee_is_air = bool(self.lockee_is_air)
+
+ @staticmethod
+ def encode(locker_id, locker_is_air, lockee_id, lockee_is_air, with_size:bool=False):
+ if isinstance(locker_is_air, bool):
+ locker_is_air = int(locker_is_air)
+ if isinstance(lockee_is_air, bool):
+ lockee_is_air = int(lockee_is_air)
+ buffer = pack("I",18)+pack("IIII", locker_id, locker_is_air, lockee_id, lockee_is_air)
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_LOGOFF.py b/lib/PacketManager/packets/FSNETCMD_LOGOFF.py
new file mode 100644
index 0000000..bc03a0b
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_LOGOFF.py
@@ -0,0 +1,19 @@
+from struct import pack
+
+class FSNETCMD_LOGOFF: #2
+ """
+ This is a logoff packet, used to logoff from the server.
+ It appeasr to be un-used by YS. Including it for completeness.
+ """
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+
+ def decode(self):
+ pass
+
+ @staticmethod
+ def encode( with_size:bool=False):
+ buffer = pack("I",2)
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_LOGON.py b/lib/PacketManager/packets/FSNETCMD_LOGON.py
new file mode 100644
index 0000000..84d6c86
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_LOGON.py
@@ -0,0 +1,42 @@
+from struct import pack, unpack
+
+class FSNETCMD_LOGON: #1
+ """
+ This is a logon packet, used to logon to the server.
+ The client sends this to the server on login, and the server replies to acknowledge.
+ """
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ self.version = None
+ self.username = None
+ self.alias = None #Alias is the longer form of the username, if they're longer than 16 chars
+
+ #If the YS version is 2018 (or YSCE) then the server will reply with an
+ # empty packet on login-complete.
+ if len(self.buffer)>5 and should_decode:
+ #We can decode the packet.
+ self.decode()
+
+ def decode(self):
+ self.username, self.version = unpack("16sI",self.buffer[4:24])
+ if len(self.buffer)>24:
+ self.alias = self.buffer[24:].decode().strip('\x00')
+ else:
+ self.alias = self.username
+
+ @staticmethod #Method to create a logon packet, if required.
+ def encode(username, version, with_size:bool=False):
+ if len(username)>15:
+ shortform = username[:15]
+ alias = username
+ else:
+ shortform = username
+ alias = None
+ buffer = pack("I16sI", 1, shortform.encode(), version)
+ if alias:
+ buffer += alias.encode()
+
+ if with_size:
+ return pack("I",len(buffer))+buffer
+
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_MISSILELAUNCH.py b/lib/PacketManager/packets/FSNETCMD_MISSILELAUNCH.py
new file mode 100644
index 0000000..7d7000a
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_MISSILELAUNCH.py
@@ -0,0 +1,64 @@
+from struct import pack, unpack
+from . import FSWEAPON_DICT, GUIDEDWEAPONS
+
+class FSNETCMD_MISSILELAUNCH: #20
+ """
+ Sent when a missile has been launched
+ """
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ self.weapon_type = None
+ self.position = [0,0,0]
+ self.atti = [0,0,0]
+ self.velocity = None
+ self.life_remaining = None
+ self.power = None
+ self.fired_by_aircraft = None
+ self.fired_by = None
+ self.v_max = None
+ self.mobility = None
+ self.radar = None
+ self.fired_at_aircraft = None
+ self.fired_at = None
+ if should_decode:
+ self.decode()
+
+ def decode(self):
+ self.weapon_type = unpack("H", self.buffer[4:6])[0]
+ self.position = list(unpack("fff", self.buffer[6:18]))
+ self.atti = list(unpack("fff", self.buffer[18:30]))
+ self.velocity, self.life_remaining, self.power = unpack("ffH", self.buffer[30:38])
+ self.fired_by_aircraft = bool(unpack("I", self.buffer[38:42])[0])
+ self.fired_by = unpack("I", self.buffer[42:46])[0]
+ if self.weapon_type in FSWEAPON_DICT:
+ self.weapon_type = FSWEAPON_DICT[self.weapon_type]
+
+ if self.weapon_type in GUIDEDWEAPONS:
+ self.v_max, self.mobility, self.radar = unpack("fff", self.buffer[46:58])
+ self.fired_at_aircraft = bool(unpack("I", self.buffer[58:62])[0])
+ self.fired_at = unpack("I", self.buffer[62:66])[0]
+ elif self.weapon_type == "FSWEAPON_FLARE":
+ self.v_max = unpack("f", self.buffer[46:50])[0]
+
+ @staticmethod
+ def encode(weapon_type, position, atti, velocity, life_remaining, power,
+ fired_by_aircraft, fired_by, v_max=None, mobility=None,
+ radar=None, fired_at_aircraft=None, fired_at=None, with_size:bool=False):
+ if weapon_type in FSWEAPON_DICT and isinstance(weapon_type,int):
+ weapon_type_name = FSWEAPON_DICT[weapon_type]
+ else:
+ weapon_type_name = weapon_type
+ weapon_type = list(FSWEAPON_DICT.keys())[list(FSWEAPON_DICT.values()).index(weapon_type)]
+ buffer = pack("I",20)+pack("Hfff", weapon_type, *position)+pack("fff", *atti)+pack("ffH", velocity, life_remaining, power)
+ buffer += pack("I",fired_by_aircraft)+pack("I",fired_by)
+ if weapon_type_name in GUIDEDWEAPONS:
+ buffer += pack("fff", v_max, mobility, radar)
+ if isinstance(fired_at_aircraft, bool):
+ fired_at_aircraft = int(fired_at_aircraft)
+ buffer += pack("II", fired_at_aircraft, fired_at)
+ if weapon_type_name == "FSWEAPON_FLARE":
+ buffer += pack("f", v_max)
+
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_NULL.py b/lib/PacketManager/packets/FSNETCMD_NULL.py
new file mode 100644
index 0000000..5e35777
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_NULL.py
@@ -0,0 +1,16 @@
+from struct import pack
+
+class FSNETCMD_NULL: #0
+ """
+ This is a 'null' packet, there is nothing to process.
+ """
+ def __init__(self,buffer:bytes, should_decode:bool=True):
+ pass
+
+ def decode(self):
+ return None
+
+ def encode(self, with_size:bool=False):
+ if with_size:
+ return pack("II",4,0)
+ return pack("I", 0)
diff --git a/lib/PacketManager/packets/FSNETCMD_PREPARESIMULATION.py b/lib/PacketManager/packets/FSNETCMD_PREPARESIMULATION.py
new file mode 100644
index 0000000..4cfef8f
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_PREPARESIMULATION.py
@@ -0,0 +1,14 @@
+from struct import pack
+from .FSNETCMD_EMPTYPACKET import FSNETCMD_EMPTYPACKET
+
+class FSNETCMD_PREPARESIMULATION(FSNETCMD_EMPTYPACKET): #16
+ """
+ This is sent from server to client when they've almost finished
+ logging in.
+ """
+ @staticmethod
+ def encode(with_size:bool=False):
+ buffer = pack("I",16)
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_READBACK.py b/lib/PacketManager/packets/FSNETCMD_READBACK.py
new file mode 100644
index 0000000..0fd4ead
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_READBACK.py
@@ -0,0 +1,39 @@
+from struct import pack, unpack
+
+class FSNETCMD_READBACK: #6
+ """
+ Sent from client to server and back to acknowledge various packets.
+
+ * Client sends FSNETREADBACK_ADDAIRPLAN or FSNETREADBACK_ADDGROUND to
+ acknowledge FSNETCMD_ADDOBJECT
+ * Client sends FSNETREADBACK_REMOVEAIRPLANE or FSNETREADBACK_REMOVEGROUND
+ to acknowledge FSNETCMD_REMOVEAIRPLANE or FSNETCMD_REMOVEGROUND
+ * Client sends FSNETREADBACK_ENVIRONMENT to acknowledge FSNETCMD_ENVIRONMENT
+ * Client sends FSNETREADBACK_JOINREQUEST to acknowledge FSNETCMD_JOINREQUEST
+ * Client sends FSNETREADBACK_PREPARE to acknowledge FSNETCMD_PREPARESIMULATION
+ * Client sends FSNETREADBACK_USEMISSILE to acknowledge FSNETCMD_USEMISSILE
+ * Client sends FSNETREADBACK_USEUNGUIDEDWEAPON to acknowledge FSNETCMD_USEUNGUIDEDWEAPON
+ * Client sends FSNETREADBACK_CTRLSHOWUSERNAME to acknowledge FSNETCMD_CTRLSHOWUSERNAME
+
+ * Server sends FSNETREADBACK_JOINREQUEST to acknowledge FSNETCMD_JOINREQUEST -
+ Will punt the user if the server receives this from them
+ * There are probably more, but I've not gone into much detail here yet.
+
+ """
+
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ self.read_back_type = None
+ self.read_back_param = None
+ if should_decode:
+ self.decode()
+
+ def decode(self):
+ self.read_back_type, _, self.read_back_param = unpack("HHI", self.buffer[4:12])
+
+ @staticmethod
+ def encode(read_back_type, read_back_param, with_size:bool=False):
+ buffer = pack("IhhI", 6, read_back_type, 0, read_back_param)
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_REJECTJOINREQ.py b/lib/PacketManager/packets/FSNETCMD_REJECTJOINREQ.py
new file mode 100644
index 0000000..e19a007
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_REJECTJOINREQ.py
@@ -0,0 +1,21 @@
+from struct import pack
+
+class FSNETCMD_REJECTJOINREQ: #10
+ """
+ If the server rejects the join request, they'll send this.
+ It's usually followed by a chat message saying why.
+ """
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ if should_decode:
+ self.decode()
+
+ def decode(self):
+ pass
+
+ @staticmethod
+ def encode(with_size:bool=False):
+ buffer = pack("I",10)
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_REMOVEAIRPLANE.py b/lib/PacketManager/packets/FSNETCMD_REMOVEAIRPLANE.py
new file mode 100644
index 0000000..b880a3e
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_REMOVEAIRPLANE.py
@@ -0,0 +1,13 @@
+from .FSNETCMD_UNJOIN import FSNETCMD_UNJOIN
+from struct import pack
+
+class FSNETCMD_REMOVEAIRPLANE(FSNETCMD_UNJOIN): #13
+ """
+ Seems to just be the same as 12. No idea why Soji made 2.
+ We'll just extend UNJOIN."""
+ @staticmethod
+ def encode(object_id, explosion, with_size:bool=False):
+ buffer = pack("I", 12)+pack("IIhh", 13, object_id, explosion)
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_REMOVEGROUND.py b/lib/PacketManager/packets/FSNETCMD_REMOVEGROUND.py
new file mode 100644
index 0000000..9861add
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_REMOVEGROUND.py
@@ -0,0 +1,13 @@
+from struct import pack
+from .FSNETCMD_UNJOIN import FSNETCMD_UNJOIN
+
+class FSNETCMD_REMOVEGROUND(FSNETCMD_UNJOIN): #19
+ """
+ This is the same as FSNETCMD_UNJOIN/Remove aircraft, but for ground objects.
+ """
+ @staticmethod
+ def encode(object_id, explosion, with_size:bool=False):
+ buffer =pack("I",19)+pack("IIhh", 19, object_id, explosion)
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_REPORTSCORE.py b/lib/PacketManager/packets/FSNETCMD_REPORTSCORE.py
new file mode 100644
index 0000000..96dfe68
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_REPORTSCORE.py
@@ -0,0 +1,50 @@
+from struct import pack, unpack
+from . import FSWEAPON_DICT
+"""
+This is the score card for when an aircraft/ground object is destroyed.
+"""
+class FSNETCMD_REPORTSCORE:
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ self.scored = None
+ self.weapon_type = None
+ self.position = [0,0,0]
+ self.score_time = None
+ self.killer_id = None
+ self.killer_name = None
+ self.killer_plane = None
+ self.victim_id = None
+ self.victim_name = None
+ self.victim_plane = None
+
+ if should_decode:
+ self.decode()
+
+ def decode(self):
+ self.scored = bool(unpack("H", self.buffer[4:6])[0])
+ self.weapon_type = unpack("H", self.buffer[6:8])[0]
+ if self.weapon_type in FSWEAPON_DICT:
+ self.weapon_type = FSWEAPON_DICT[self.weapon_type]
+ self.position = list(unpack("fff", self.buffer[8:20]))
+ self.score_time = unpack("f", self.buffer[20:24])[0]
+ self.killer_id = unpack("I", self.buffer[28:32])[0]
+ self.killer_name = self.buffer[32:64].decode("utf-8").rstrip("\x00")
+ self.killer_plane = self.buffer[64:96].decode("utf-8").rstrip("\x00")
+ self.victim_id = unpack("I", self.buffer[100:104])[0]
+ self.victim_name = self.buffer[104:136].decode("utf-8").rstrip("\x00")
+ self.victim_plane = self.buffer[136:168].decode("utf-8").rstrip("\x00")
+
+ @staticmethod
+ def encode(scored, weapon_type, position, score_time, killer_id, killer_name,
+ killer_plane, victim_id, victim_name, victim_plane, with_size:bool=False):
+ if weapon_type in FSWEAPON_DICT and not isinstance(weapon_type,int):
+ weapon_type = list(FSWEAPON_DICT.keys())[list(FSWEAPON_DICT.values()).index(weapon_type)]
+
+ buffer = pack("IHHffffII32s32sII32s32s", 46,
+ int(scored), weapon_type, *position, score_time, 0, killer_id,
+ killer_name.encode("utf-8"), killer_plane.encode("utf-8"),0,
+ victim_id, victim_name.encode("utf-8"), victim_plane.encode("utf-8"))
+ if with_size:
+ return pack("I", len(buffer))+buffer
+
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_REQUESTTESTAIRPLANE.py b/lib/PacketManager/packets/FSNETCMD_REQUESTTESTAIRPLANE.py
new file mode 100644
index 0000000..f92667b
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_REQUESTTESTAIRPLANE.py
@@ -0,0 +1,14 @@
+from struct import pack
+from .FSNETCMD_EMPTYPACKET import FSNETCMD_EMPTYPACKET
+
+class FSNETCMD_REQUESTTESTAIRPLANE(FSNETCMD_EMPTYPACKET): #14
+ """
+ Spawns an F-15C at NORTH1000_01 in dogfight mode
+ There is no way of calling this from YS.
+ """
+ @staticmethod
+ def encode(with_size:bool=False):
+ buffer = pack("I",14)
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_SERVER_FORCE_JOIN.py b/lib/PacketManager/packets/FSNETCMD_SERVER_FORCE_JOIN.py
new file mode 100644
index 0000000..a94aa17
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_SERVER_FORCE_JOIN.py
@@ -0,0 +1,14 @@
+from struct import pack, unpack
+from .FSNETCMD_NULL import FSNETCMD_NULL
+
+class FSNETCMD_SERVER_FORCE_JOIN(FSNETCMD_NULL):
+ """
+ Sent by the server to force the player to join
+ This literally just presses J. Wild.
+ """
+ @staticmethod
+ def encode(player_id, with_size:bool=False):
+ buffer = pack("I", 47)
+ if with_size:
+ return pack("I", len(buffer)) + buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_SMOKECOLOR.py b/lib/PacketManager/packets/FSNETCMD_SMOKECOLOR.py
new file mode 100644
index 0000000..76e254d
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_SMOKECOLOR.py
@@ -0,0 +1,25 @@
+from struct import unpack, pack
+
+class FSNETCMD_SMOKECOLOR: #7
+ """The server sends this to the client when another aircraft
+ joins with smoke, and the client sends it to the server if
+ they're joining with smoke.
+ """
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ self.aircraft_id = None
+ self.smoke_quantity = None
+ self.color = None
+ if should_decode:
+ self.decode()
+
+ def decode(self):
+ self.aircraft_id, self.smoke_quantity, r, g, b = unpack("IBBBB", self.buffer[4:9])
+ self.color = (r,g,b)
+
+ @staticmethod
+ def encode(aircraft_id, smoke_quantity, color, with_size:bool=False):
+ buffer = pack("IIBBBB", 7, aircraft_id, smoke_quantity, color[0], color[1], color[2])
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_TESTPACKET.py b/lib/PacketManager/packets/FSNETCMD_TESTPACKET.py
new file mode 100644
index 0000000..58c9bfc
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_TESTPACKET.py
@@ -0,0 +1,14 @@
+from struct import pack
+from .FSNETCMD_EMPTYPACKET import FSNETCMD_EMPTYPACKET
+
+class FSNETCMD_TESTPACKET(FSNETCMD_EMPTYPACKET): #17
+ """
+ This is just an empty packet.
+ Just overwrite the encode.
+ """
+ @staticmethod
+ def encode(with_size:bool=False):
+ buffer = pack("I",17)
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/FSNETCMD_TEXTMESSAGE.py b/lib/PacketManager/packets/FSNETCMD_TEXTMESSAGE.py
new file mode 100644
index 0000000..4ff987d
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_TEXTMESSAGE.py
@@ -0,0 +1,24 @@
+from struct import pack
+import re
+
+class FSNETCMD_TEXTMESSAGE: #32
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ self.raw_message = None
+ self.user = None
+ self.message = None
+ if should_decode:
+ self.decode()
+
+ def decode(self):
+ self.raw_message = self.buffer[12:].decode("utf-8").strip("\x00")
+ match = re.match(r"^\(([^)]+)\)(.+)", self.raw_message)
+ if match:
+ self.user. self.message = match.groups()
+
+ @staticmethod
+ def encode(message:str, with_size:bool=False):
+ buffer = pack("I",32)+message.encode("utf-8")+b"\x00"
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer
diff --git a/lib/PacketManager/packets/FSNETCMD_UNJOIN.py b/lib/PacketManager/packets/FSNETCMD_UNJOIN.py
new file mode 100644
index 0000000..1f991ae
--- /dev/null
+++ b/lib/PacketManager/packets/FSNETCMD_UNJOIN.py
@@ -0,0 +1,26 @@
+from struct import pack, unpack
+
+class FSNETCMD_UNJOIN: #12
+ """
+ This is sent client to server when the client leaves.
+ The explosion doesn't seem to actually do anything in YS.
+ There is a comment saying "add explosion here"... Not helpful.
+ """
+ def __init__(self, buffer:bytes, should_decode:bool=True):
+ self.buffer = buffer
+ self.object_id = None
+ self.explosion = None
+ if should_decode:
+ self.decode()
+
+ def decode(self):
+ variables = unpack("IIhh", self.buffer[0:12])
+ self.object_id = variables[1]
+ self.explosion = bool(variables[2])
+
+ @staticmethod
+ def encode(object_id, explosion, with_size:bool=False):
+ buffer = pack("I", 12)+pack("IIhh", 12, object_id, explosion)
+ if with_size:
+ return pack("I",len(buffer))+buffer
+ return buffer \ No newline at end of file
diff --git a/lib/PacketManager/packets/__init__.py b/lib/PacketManager/packets/__init__.py
new file mode 100644
index 0000000..007c6b4
--- /dev/null
+++ b/lib/PacketManager/packets/__init__.py
@@ -0,0 +1,472 @@
+#Pass explicits here
+
+MESSAGE_TYPES = [
+ "FSNETCMD_NULL", # 0
+ "FSNETCMD_LOGON", # 1 Cli ->Svr", (Svr->Cli for log-on complete acknowledgement.)
+ "FSNETCMD_LOGOFF", # 2
+ "FSNETCMD_ERROR", # 3
+ "FSNETCMD_LOADFIELD", # 4 Svr ->Cli", Cli->Svr for read back
+ "FSNETCMD_ADDOBJECT", # 5 Svr ->Cli
+ "FSNETCMD_READBACK", # 6 Svr<->Cli
+ "FSNETCMD_SMOKECOLOR", # 7 Svr<->Cli
+ "FSNETCMD_JOINREQUEST", # 8 Svr<- Cli
+ "FSNETCMD_JOINAPPROVAL", # 9 Svr ->Cli
+ "FSNETCMD_REJECTJOINREQ", # 10
+ "FSNETCMD_AIRPLANESTATE", # 11 Svr<->Cli # Be careful in FsDeleteOldStatePacket when modify
+ "FSNETCMD_UNJOIN", # 12 Svr<- Cli
+ "FSNETCMD_REMOVEAIRPLANE", # 13 Svr<->Cli
+ "FSNETCMD_REQUESTTESTAIRPLANE", # 14
+ "FSNETCMD_KILLSERVER", # 15 Svr<- Cli
+ "FSNETCMD_PREPARESIMULATION", # 16 Svr ->Cli
+ "FSNETCMD_TESTPACKET", # 17
+ "FSNETCMD_LOCKON", # 18 Svr<->Cli
+ "FSNETCMD_REMOVEGROUND", # 19 Svr<->Cli
+ "FSNETCMD_MISSILELAUNCH", # 20 Svr<->Cli # fsweapon.cpp is responsible for encoding/decoding
+ "FSNETCMD_GROUNDSTATE", # 21 Svr<->Cli # Be careful in FsDeleteOldStatePacket when modify
+ "FSNETCMD_GETDAMAGE", # 22 Svr<->Cli
+ "FSNETCMD_GNDTURRETSTATE", # 23 Svr<->Cli
+ "FSNETCMD_SETTESTAUTOPILOT", # 24 Svr ->Cli
+ "FSNETCMD_REQTOBESIDEWINDOWOFSVR", # 25 Svr<- Cli
+ "FSNETCMD_ASSIGNSIDEWINDOW", # 26 Svr ->Cli
+ "FSNETCMD_RESENDAIRREQUEST", # 27 Svr<- Cli
+ "FSNETCMD_RESENDGNDREQUEST", # 28 Svr<- Cli
+ "FSNETCMD_VERSIONNOTIFY", # 29 Svr ->Cli
+ "FSNETCMD_AIRCMD", # 30 Svr<->Cli # After 2001/06/24
+ "FSNETCMD_USEMISSILE", # 31 Svr ->Cli # After 2001/06/24
+ "FSNETCMD_TEXTMESSAGE", # 32 Svr<->Cli
+ "FSNETCMD_ENVIRONMENT", # 33 Svr<->Cli (*1)
+ "FSNETCMD_NEEDRESENDJOINAPPROVAL", # 34 Svr<- Cli
+ "FSNETCMD_REVIVEGROUND", # 35 Svr ->Cli # After 2004
+ "FSNETCMD_WEAPONCONFIG", # 36 Svr<->Cli # After 20040618
+ "FSNETCMD_LISTUSER", # 37 Svr<->Cli # After 20040726
+ "FSNETCMD_QUERYAIRSTATE", # 38 Cli ->Svr # After 20050207
+ "FSNETCMD_USEUNGUIDEDWEAPON", # 39 Svr ->Cli # After 20050323
+ "FSNETCMD_AIRTURRETSTATE", # 40 Svr<->Cli # After 20050701
+ "FSNETCMD_CTRLSHOWUSERNAME", # 41 Svr ->Cli # After 20050914
+ "FSNETCMD_CONFIRMEXISTENCE", # 42 Not Used
+ "FSNETCMD_CONFIGSTRING", # 43 Svr ->Cli # After 20060514 Cli->Svr for read back
+ "FSNETCMD_LIST", # 44 Svr ->Cli # After 20060514 Cli->Svr for read back
+ "FSNETCMD_GNDCMD", # 45 Svr<->Cli
+ "FSNETCMD_REPORTSCORE", # 46 Svr -> Cli # After 20100630 (Older version will ignore)
+ "FSNETCMD_SERVER_FORCE_JOIN", # 47 Svr -> Cli
+ "FSNETCMD_FOGCOLOR", # 48 Svr -> Cli
+ "FSNETCMD_SKYCOLOR", # 49 Svr -> Cli
+ "FSNETCMD_GNDCOLOR", # 50 Svr -> Cli
+ "FSNETCMD_RESERVED_FOR_LIGHTCOLOR",# 51 Svr -> Cli
+ "FSNETCMD_GENERATEATTACKER", # 52
+ "FSNETCMD_RESERVED22", # 53
+ "FSNETCMD_RESERVED23", # 54
+ "FSNETCMD_RESERVED24", # 55
+ "FSNETCMD_RESERVED25", # 56
+ "FSNETCMD_RESERVED26", # 57
+ "FSNETCMD_RESERVED27", # 58
+ "FSNETCMD_RESERVED28", # 59
+ "FSNETCMD_RESERVED29", # 60
+ "FSNETCMD_RESERVED30", # 61
+ "FSNETCMD_RESERVED31", # 62
+ "FSNETCMD_RESERVED32", # 63
+ "FSNETCMD_OPENYSF_RESERVED33", # 64 Reserved for OpenYSF
+ "FSNETCMD_OPENYSF_RESERVED34", # 65 Reserved for OpenYSF
+ "FSNETCMD_OPENYSF_RESERVED35", # 66 Reserved for OpenYSF
+ "FSNETCMD_OPENYSF_RESERVED36", # 67 Reserved for OpenYSF
+ "FSNETCMD_OPENYSF_RESERVED37", # 68 Reserved for OpenYSF
+ "FSNETCMD_OPENYSF_RESERVED38", # 69 Reserved for OpenYSF
+ "FSNETCMD_OPENYSF_RESERVED39", # 70 Reserved for OpenYSF
+ "FSNETCMD_OPENYSF_RESERVED40", # 71 Reserved for OpenYSF
+ "FSNETCMD_OPENYSF_RESERVED41", # 72 Reserved for OpenYSF
+ "FSNETCMD_OPENYSF_RESERVED42", # 73 Reserved for OpenYSF
+ "FSNETCMD_RESERVED43", # 74
+ "FSNETCMD_RESERVED44", # 75
+ "FSNETCMD_RESERVED45", # 76
+ "FSNETCMD_RESERVED46", # 77
+ "FSNETCMD_RESERVED47", # 78
+ "FSNETCMD_RESERVED48", # 79
+ "FSNETCMD_RESERVED49", # 80
+ "FSNETCMD_NOP"
+]
+
+READBACKS = ["FSNETREADBACK_ADDAIRPLANE",
+ "FSNETREADBACK_ADDGROUND",
+ "FSNETREADBACK_REMOVEAIRPLANE",
+ "FSNETREADBACK_REMOVEGROUND",
+ "FSNETREADBACK_ENVIRONMENT",
+ "FSNETREADBACK_JOINREQUEST",
+ "FSNETREADBACK_JOINAPPROVAL",
+ "FSNETREADBACK_PREPARE",
+ "FSNETREADBACK____UNUSED____",
+ "FSNETREADBACK_USEMISSILE",
+ "FSNETREADBACK_USEUNGUIDEDWEAPON",
+ "FSNETREADBACK_CTRLSHOWUSERNAME"]
+
+FSWEAPON_DICT = {
+ 0: "FSWEAPON_GUN",
+ 1: "FSWEAPON_AIM9",
+ 2: "FSWEAPON_AGM65",
+ 3: "FSWEAPON_BOMB",
+ 4: "FSWEAPON_ROCKET",
+ 5: "FSWEAPON_FLARE",
+ 6: "FSWEAPON_AIM120",
+ 7: "FSWEAPON_BOMB250",
+ 8: "FSWEAPON_SMOKE",
+ 9: "FSWEAPON_BOMB500HD",
+ 10: "FSWEAPON_AIM9X",
+ 11: "FSWEAPON_FLAREPOD",
+ 12: "FSWEAPON_FUELTANK",
+ 13: "FSWEAPON_RESERVED13",
+ 14: "FSWEAPON_RESERVED14",
+ 15: "FSWEAPON_RESERVED15",
+ 16: "FSWEAPON_RESERVED16",
+ 17: "FSWEAPON_RESERVED17",
+ 18: "FSWEAPON_RESERVED18",
+ 19: "FSWEAPON_RESERVED19",
+ 20: "FSWEAPON_RESERVED20",
+ 21: "FSWEAPON_RESERVED21",
+ 22: "FSWEAPON_RESERVED22",
+ 23: "FSWEAPON_RESERVED23",
+ 24: "FSWEAPON_RESERVED24",
+ 25: "FSWEAPON_RESERVED25",
+ 26: "FSWEAPON_RESERVED26",
+ 27: "FSWEAPON_RESERVED27",
+ 28: "FSWEAPON_RESERVED28",
+ 29: "FSWEAPON_RESERVED29",
+ 30: "FSWEAPON_RESERVED30",
+ 31: "FSWEAPON_RESERVED31",
+ 32: "FSWEAPON_SMOKE0",
+ 33: "FSWEAPON_SMOKE1",
+ 34: "FSWEAPON_SMOKE2",
+ 35: "FSWEAPON_SMOKE3",
+ 36: "FSWEAPON_SMOKE4",
+ 37: "FSWEAPON_SMOKE5",
+ 38: "FSWEAPON_SMOKE6",
+ 39: "FSWEAPON_SMOKE7",
+ 40: "FSWEAPON_RESERVED40",
+ 41: "FSWEAPON_RESERVED41",
+ 42: "FSWEAPON_RESERVED42",
+ 43: "FSWEAPON_RESERVED43",
+ 44: "FSWEAPON_RESERVED44",
+ 45: "FSWEAPON_RESERVED45",
+ 46: "FSWEAPON_RESERVED46",
+ 47: "FSWEAPON_RESERVED47",
+ 48: "FSWEAPON_NUMWEAPONTYPE",
+ 127: "FSWEAPON_NULL",
+ 128: "FSWEAPON_DEBRIS",
+ 200: "FSWEAPON_FLARE_INTERNAL"
+}
+
+GUIDEDWEAPONS = missiles = ["FSWEAPON_AGM65", "FSWEAPON_AIM9", "FSWEAPON_AIM120",
+ "FSWEAPON_AIM9X", "FSWEAPON_ROCKET"]
+
+ERROR_CODES = ["FSNETERR_NOERR",
+ "FSNETERR_VERSIONCONFLICT",
+ "FSNETERR_CANNOTADDOBJECT",
+ "FSNETERR_REJECT",
+ "FSNETERR_CANNOTSUSTAIN"]
+
+AIRCMD_KEYWORDS = [
+ "AFTBURNR", #TRUE/FALSE HAS AFTERBURNER
+ "THRAFTBN", ###[N][KG][LB] AFTERBURNER POWER
+ "THRMILIT", ###[N][KG][LB] MILITARY POWER
+ "WEIGHCLN", ###[KG][LB] CLEAN WEIGHT
+ "WEIGFUEL", ###[KG][LB] MAX WEIGHT OF FUEL
+ "WEIGLOAD", ###[KG][LB] MAX WEIGHT OF PAYLOAD
+ "FUELABRN", ###[KG][LB] FUEL CONSUMPTION/SEC WHEN BURNER ON
+ "FUELMILI", ###[KG][LB] FUEL CONSUMPTION/SEC WHEN MIL POWER
+
+ "LEFTGEAR", #X Y Z [M][IN] LEFT MAIN GEAR POSITION
+ "RIGHGEAR", #X Y Z [M][IN] RIGHT MAIN GEAR POSITION
+ "WHELGEAR", #X Y Z [M][IN] WHEEL POSITION
+
+ "CRITAOAP", ###[RAD][DEG] CRITICAL AOA (PLUS)
+ "CRITAOAM", ###[RAD][DEG] CRITICAL AOA (MINUS
+
+ "CRITSPED", ###[KT][KM/H][M/S][MACH]CRITICAL AIRSPEED
+ "MAXSPEED", ###[KT][KM/H][M/S][MACH]MAXIMUM AIRSPEED
+
+ "HASSPOIL", #TRUE/FALSE HAS SPOILER
+ "RETRGEAR", #TRUE/FALSE LANDING GEAR IS RETRACTABLE
+ "VARGEOMW", #TRUE/FALSE HAS VARIABLE GEOMETRY WING
+
+ "CLVARGEO", ###(DIMENSIONLESS) INCREASE OF CL WHEN VGW IS EXTENDED
+ "CDVARGEO", ###(DIMENSIONLESS) INCREASE OF CD WHEN VGW IS EXTENDED
+ "CLBYFLAP", ###(DIMENSIONLESS) INCREASE OF CL WHEN FLAP FULL DOWN
+ "CDBYFLAP", ###(DIMENSIONLESS) INCREASE OF CD WHEN FLAP FULL DOWN
+ "CDBYGEAR", ###(DIMENSIONLESS) INCREASE OF CD WHEN GEAR DOWN
+ "CDSPOILR", ###(DIMENSIONLESS) INCREASE OF CD WHEN SPOILER IS DEPLOYED
+
+ "WINGAREA", ###[M^2][IN^2] AREA OF WING
+
+ "MXIPTAOA", ###[RAD][DEG] MAX INPUT AOA
+ "MXIPTSSA", ###[RAD][DEG] MAX INPUT YAW
+ "MXIPTROL", ###[RAD][DEG] MAX INPUT ROLL RATIO
+
+ "CPITMANE", ###(DIMENSIONLESS) PITCH MANEUVABILITY CONSTANT
+ "CPITSTAB", ###(DIMENSIONLESS) PITCH STABILITY CONSTANT
+ "CYAWMANE", ###(DIMENSIONLESS) YAW MANEUVABILITY CONSTANT
+ "CYAWSTAB", ###(DIMENSIONLESS) YAW STABILITY CONSTANT
+ "CROLLMAN", ###(DIMENSIONLESS) ROLL MANEUVABILITY CONSTANT
+
+
+ "CTLLDGEA", #TRUE/FALSE INITIAL GEAR
+ "CTLBRAKE", #TRUE/FALSE INITIAL BRAKE
+ "CTLSPOIL", #0.0-1.0 INITIAL SPOILER
+ "CTLABRNR", #TRUE/FALSE INITIAL AFTERBURNER
+ "CTLTHROT", #0.0-1.0 INITIAL THROTTLE
+ "CTLIFLAP", #0.0-1.0 INITIAL FLAP
+ "CTLINVGW", #0.0-1.0 INITIAL VGW
+ "CTLATVGW", #TRUE/FALSE INITIAL AUTO VGW
+
+ "POSITION", #X Y Z [M][IN]
+ "ATTITUDE", #H P B [DEG][RAD]
+ "INITFUEL", ###[KG][LB]
+ "INITLOAD", ###[KG][LB]
+ "INITSPED", ###[M/S][KT][MACH]
+
+
+
+ "REFVCRUS", ###[M/S][KM/H][KT] CRUISING SPEED
+ "REFACRUS", ###[M][FT] CRUISING ALTITUDE
+ "REFVLAND", ###[M/S][KM/H][KT] LANDING SPEED
+ "REFAOALD", ###[DEG][RAD] AOA WHILE APPROACHING
+ "REFLNRWY", ###[M][FT][KM] RUNWAY LENGTH REQUIRED TO LAND
+
+ "REM",
+
+ "COCKPITP",
+ "REFTHRLD",
+ "REFTCRUS",
+
+ "AUTOCALC",
+
+ "IDENTIFY",
+
+ "MANESPD1",
+ "MANESPD2",
+
+ "MACHNGUN",
+ "SMOKEGEN",
+ "HTRADIUS",
+ "TRIGGER1",
+ "TRIGGER2",
+ "TRIGGER3",
+ "TRIGGER4",
+
+ "STRENGTH",
+
+ "PROPELLR",
+
+ "VAPORPO0",
+ "VAPORPO1",
+
+ "INITIGUN",
+ "INITIAAM",
+ "INITIAGM",
+
+ "MANESPD3",
+
+ "RADARCRS",
+
+ "MACHNGN2",
+
+ "SMOKEOIL",
+ "WEAPONCH",
+ "INITBOMB",
+
+ "MONTRILS",
+
+ "GUNPOWER",
+
+ "CATEGORY", # Normal,Utility or Aerobatic (+fighter, attacker)
+
+ "VGWSPED1", # Auto Vgw Reference Speed (Slower Speed)
+ "VGWSPED2", # Auto Vgw Reference Speed (Faster Speed)
+
+ "GUNDIREC", # GUN direction
+
+ # 2001/05/06 >>
+ "INITRCKT", # Initial number of rockets
+ "MAXNMGUN", # chMaxNumGunBullet
+ "MAXNMAAM", # chMaxNumAAM Deprecated 2010/08/04
+ "MAXNMAGM", # chMaxNumAGM Deprecated 2010/08/04
+ "MAXNMRKT", # chMaxNumRocket Deprecated 2010/08/04
+
+ # 2001/06/05 >>
+ "AAMSLOT_", # chAAMSlot[chNumAAMSlot++]
+ "AGMSLOT_", # chAGMSlot[chNumAGMSlot++]
+ "RKTSLOT_", # chRocketSlot[chNumRocketSlot++]
+ "BOMBSLOT", # chBombSlot[chNumBombSlot++]
+ "AAMVISIB", # chAAMVisible;
+ "AGMVISIB", # chAGMVisible;
+ "BOMVISIB", # chBombVisible;
+ "RKTVISIB", # chRocketVisible
+ "MAXNBOMB", # chMaxNumBomb Deprecated 2010/08/04
+
+ # 2002/12/11 >>
+ "ARRESTER", # chArrestingHook
+
+ # 2003/02/02 >>
+ "TRSTVCTR", # chHasThrustVector
+ "TRSTDIR0", # chThrVec0
+ "TRSTDIR1", # chThrVec1
+ "PSTMPTCH", # Post-Stall VPitch
+ "PSTMYAW_", # Post-Stall VYaw
+ "PSTMROLL", # Post-Stall VRoll
+
+ # 2003/02/12
+ "AIRCLASS", # Aircraft class
+
+ # 2003#02/15
+ "PROPEFCY", # Propeller efficiency
+ "PROPVMIN", # Minimum speed that T=P/v becomes valid
+
+ # 2003/09/19
+ "VRGMNOSE", # Variable Geometry Nose : Concorde only
+
+
+ # 2003/11/25
+ "THRSTREV", # Effectiveness of the Thrust Reverser
+
+
+ # 2004/05/22
+ "GUNSIGHT", # Lead Gun Sight
+
+
+ # 2004/06/14
+ "HRDPOINT", # Defining a hardpoint
+ "LOADWEPN", # Load weapons
+ "LMTBYHDP", # Limit weapons by hardpoint definition.
+ "UNLOADWP", # Unload All Weapons (Missiles, Bombs, Rockets. Excluding Guns, Smokes, and Flare)
+
+ # 2005/01/03
+ "INSTPANL", # Draw an instrument panel instead of a hud. (av[1] for inst panel definition file.)
+
+ # 2005/01/05
+ "MACHNGN3",
+ "MACHNGN4",
+ "MACHNGN5",
+ "MACHNGN6",
+ "MACHNGN7",
+ "MACHNGN8",
+
+ # 2005/01/11
+ "BOMINBAY",
+ "BMBAYRCS",
+
+ # 2005/01/23
+ "INITAAMM", # Mid-Range AAM
+ "MAXNAAMM", # Max # Mid-Range AAM
+ "INITB250", # 250lb Bomb
+ "MAXNB250", # Max # 250lb Bomb
+
+ # 2005/03/08
+ "GUNINTVL", # Gun Interval
+
+ # 2005/06/26
+ "NMTURRET", # Number of turret
+ "TURRETPO", # 0 0m -0.8m 2.7m 0deg 0deg 0deg # Number x y z h p b
+ "TURRETPT", # 0 -40deg 0deg 0deg # Number MinPitch MaxPitch NeutralPitch
+ "TURRETHD", # 0 -120deg 120deg 0deg # Number MinHdg MaxHdg NeutralHdg
+ "TURRETAM", # 0 0 # Ammo(zero -> staGunBullet will be used)
+ "TURRETIV", # 0 0.5sec # Number ShootingInterval
+ "TURRETNM", # 0 GUN # DNM Node Name
+ "TURRETAR", # 0 FALSE # TRUE -> Anti Air Capable
+ "TURRETGD", # 0 TRUE # TRUE -> Anti Ground Capable
+ "TURRETCT", # "PILOT" or "GUNNER"
+ "TURRETRG", # Range
+ # 2005/09/28
+ "TURRETNH", # DNM Node Name (Heading Rotation)
+ "TURRETNP", # DNM Node Name (Pitch Rotation)
+
+ # 2006/04/25
+ "SETCNTRL", # Set Control eg. ILS TRIM:0.3 etc.
+
+ # 2006/07/19
+ "EXCAMERA", # Extra Camera
+
+ # 2006/08/05
+ "NMACHNGN", # Number of machine guns.
+
+ # 2007/04/06
+ "SMOKECOL", # Smoke Color #dmy# R G B
+
+ # 2007/09/16
+ "SUBSTNAM", # Substitute airplane (In case the airplane was not installed)
+
+ # 2010/06/26
+ "ISPNLPOS", # Instrument Panel Position
+ "ISPNLSCL", # Instrument Panel Scaling
+
+ # 2010/06/29
+ "ISPNLHUD", # Use both inst panel and HUD
+ "COCKPITA", # Neutral Head Direction
+
+ # 2010/06/30
+ "SCRNCNTR", # Screen center (Relative. (-1.0,-1.0)-(1.0,1.0)
+ "ISPNLATT", # Instrument Panel Orientation
+ "MAXNMFLR", # Maximum number of flare
+
+ # 2010/07/01
+ "FLAPPOSI", # Flap position
+ "FLAREPOS", # Flare Dispenser Position and Direction
+
+ # 2010/12/11
+ "INITAAAM", # Initialize AIM9X
+ "INITHDBM", # Initialize High-Drag bomb
+ "ULOADAAM", # Unload all AAMs
+ "ULOADAGM", # Unload all AGMs
+ "ULOADBOM", # Unload all Bombs
+ "ULOADFLR", # Unload all Flare
+ "ULOADGUN", # Unload all Gun
+ "ULOADRKT", # Unload all Rocket
+
+ # 2011/12/25
+ "LOOKOFST", # Look-at Offset
+
+ # 2012/02/02
+ "WPNSHAPE", # Weapon-shape override
+
+ # 2012/02/21
+ "GEARHORN", # Landing-gear warning horn
+ "STALHORN", # Stall-warning horn
+
+ # 2013/04/14
+ "CKPITIST", # To Make inst panel available in only one of EXCAMERAs, it can be hidden in the default cockpit view.
+ "CKPITHUD", # To Make HUD available in only one of EXCAMERAs, it can be hidden in the default cockpit view.
+
+ # 2013/04/25
+ "MALFUNCT", # Malfunction
+ "REPAIRAL", # Repair all
+
+ # 2013/04/25
+ "REPAIRFN", # Repair functionality
+
+ # 2013/06/02
+ "NOLDGFLR", # No landing flare
+
+ # 2014/06/05
+ "NREALPRP", # Number of (real) propeller engines
+ "REALPROP", # Support for realistic propeller engine
+
+ # 2014/06/13
+ "TIREFRIC", # Tire friction coefficient
+
+ # 2014/06/24
+ "PSTMSPD1", # Maximum speed that the direct attitude control is fully effective.
+ "PSTMSPD2", # Speed at which the direct attitude control becomes ineffective.
+ "PSTMPWR1", # Minimum required power setting for direct attitude control
+ "PSTMPWR2", # Power setting at which the direct attitude control is fully effective
+
+ # 2014/07/11
+ "MAXCDAOA",
+ "FLATCLR1",
+ "FLATCLR2",
+ "CLDECAY1",
+ "CLDECAY2",
+
+ # 2014/10/17
+ "AIRSTATE", # I'm shocked that I didn't have it yet.
+
+ # 2018/10/07
+ "INITZOOM", # Initial zoom factor
+
+ None
+] \ No newline at end of file