From a2013cf0259920b04dd2d9053d3c46ebf5b1fc8d Mon Sep 17 00:00:00 2001 From: Skipper Date: Thu, 30 Jan 2025 21:58:35 +0000 Subject: Created PacketManager Created packet manager, and begun implementation for decoding/encoding packets --- lib/PacketManager.py | 389 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100644 lib/PacketManager.py (limited to 'lib') diff --git a/lib/PacketManager.py b/lib/PacketManager.py new file mode 100644 index 0000000..d149077 --- /dev/null +++ b/lib/PacketManager.py @@ -0,0 +1,389 @@ +from struct import unpack, pack + +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" +} + +ERROR_CODES = ["FSNETERR_NOERR", + "FSNETERR_VERSIONCONFLICT", + "FSNETERR_CANNOTADDOBJECT", + "FSNETERR_REJECT", + "FSNETERR_CANNOTSUSTAIN"] + +def decode(buffer:bytes, with_size:bool=False): + """ + Takes incomming packets from client or server and decodes them. + """ + + if with_size: + # Strips away the size, so we've just got the message. + buffer = buffer[4:] + + # Pull out the message type + msg_type = unpack("I", buffer[:4])[0] + + # Find message type on the message_types list + if msg_type < len(MESSAGE_TYPES): + msg_type = MESSAGE_TYPES[msg_type] + else: + return "Unknown message type: {}".format(msg_type) + + +class FSNETCMD_NULL: #0 + """ + This is a 'null' packet, there is nothing to process. + """ + def __init__(self,buffer:bytes): + 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) + +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): + 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: + #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 + +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): + 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 + +class FSNETCMD_ERROR: #3 + def __init__(self, buffer:bytes): + self.buffer = buffer + self.error_code = None + self.error_message = None + + 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 + +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): + 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: + 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 + +class FSNETCMD_ADDOBJECT: #5 + """ + This packet is sent by the server to add an object to the client. + """ + def __init__(self, buffer:bytes): + 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: + 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, padding = 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 + +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 + + + """ + + def __init__(self, buffer:bytes): + self.buffer = buffer + self.read_back_type = None + self.read_back_param + + self.decode() + + def decode(self): + self.read_back_type, padding, 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 + -- cgit v1.2.3 From 4448aa54cd04ac73d9ab194c77cc622f68f8d3d0 Mon Sep 17 00:00:00 2001 From: Skipper Date: Fri, 31 Jan 2025 21:21:27 +0000 Subject: Work on packet manager And refactored some of the YSplayer bits --- lib/PacketManager.py | 708 +++++++++++++++++++++++++++++++++++++++++++++++---- lib/YSplayer.py | 30 ++- proxy.py | 6 +- 3 files changed, 682 insertions(+), 62 deletions(-) (limited to 'lib') diff --git a/lib/PacketManager.py b/lib/PacketManager.py index d149077..dcd1a8d 100644 --- a/lib/PacketManager.py +++ b/lib/PacketManager.py @@ -1,4 +1,5 @@ from struct import unpack, pack +from math import pi MESSAGE_TYPES = [ "FSNETCMD_NULL", # 0 @@ -153,59 +154,86 @@ FSWEAPON_DICT = { 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"] -def decode(buffer:bytes, with_size:bool=False): +def decode(buffer:bytes, with_size:bool=False, should_decode=True): """ Takes incomming packets from client or server and decodes them. + If with_size is True, then the packet size is included in the buffer. + If decode is False, then the packet is just returned as is. + If it is true, then the packet will be decoded. """ - + if with_size: # Strips away the size, so we've just got the message. buffer = buffer[4:] - + # Pull out the message type msg_type = unpack("I", buffer[:4])[0] # Find message type on the message_types list if msg_type < len(MESSAGE_TYPES): msg_type = MESSAGE_TYPES[msg_type] - else: - return "Unknown message type: {}".format(msg_type) - + packet_class = None + if msg_type == "FSNETCMD_NULL": + packet_class = FSNETCMD_NULL + elif msg_type == "FSNETCMD_LOGON": + packet_class = FSNETCMD_LOGON + elif msg_type == "FSNETCMD_LOGOFF": + packet_class = FSNETCMD_LOGOFF + elif msg_type == "FSNETCMD_ERROR": + packet_class = FSNETCMD_ERROR + elif msg_type == "FSNETCMD_LOADFIELD": + packet_class = FSNETCMD_LOADFIELD + elif msg_type == "FSNETCMD_ADDOBJECT": + packet_class = FSNETCMD_ADDOBJECT + elif msg_type == "FSNETCMD_READBACK": + packet_class = FSNETCMD_READBACK + + if packet_class: + return packet_class(buffer, should_decode) + return None + + return None + class FSNETCMD_NULL: #0 """ This is a 'null' packet, there is nothing to process. """ - def __init__(self,buffer:bytes): + 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) + 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): + 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: + 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() @@ -215,7 +243,7 @@ class FSNETCMD_LOGON: #1 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: @@ -230,20 +258,21 @@ class FSNETCMD_LOGON: #1 if with_size: return pack("I",len(buffer))+buffer - + return buffer + 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): + 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) @@ -251,19 +280,20 @@ class FSNETCMD_LOGOFF: #2 return pack("I",len(buffer))+buffer return buffer + class FSNETCMD_ERROR: #3 - def __init__(self, buffer:bytes): + def __init__(self, buffer:bytes, should_decode:bool=True): self.buffer = buffer self.error_code = None self.error_message = None - - self.decode() - + 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) @@ -271,36 +301,40 @@ class FSNETCMD_ERROR: #3 return pack("I",len(buffer))+buffer return buffer + 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. + 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): + 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: + 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]) + 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 + class FSNETCMD_ADDOBJECT: #5 """ This packet is sent by the server to add an object to the client. """ - def __init__(self, buffer:bytes): + def __init__(self, buffer:bytes, should_decode:bool=True): self.buffer = buffer self.object_type = None self.net_type = None @@ -318,15 +352,17 @@ class FSNETCMD_ADDOBJECT: #5 self.aircraft_category = None self.pilot = None - if len(buffer)>=120: + 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_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, padding = unpack("HH", self.buffer[12:16]) + 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') @@ -336,28 +372,37 @@ class FSNETCMD_ADDOBJECT: #5 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]) + 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) + 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) + 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 + 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_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 @@ -365,25 +410,584 @@ class FSNETCMD_READBACK: #6 * 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 - + * 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): + def __init__(self, buffer:bytes, should_decode:bool=True): self.buffer = buffer self.read_back_type = None - self.read_back_param + self.read_back_param = None + if should_decode: + self.decode() - self.decode() - def decode(self): - self.read_back_type, padding, self.read_back_param = unpack("HHI", self.buffer[4:12]) - + 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) + buffer = pack("IhhI", 6, read_back_type, 0, read_back_param) + if with_size: + return pack("I",len(buffer))+buffer + return buffer + + +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 + + +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 + + +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 + + +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 + +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("fffhhhfff", *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 + +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 + + +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 + + +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 + + +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 + + +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 + + +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 + + +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 + +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 + + +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 + + +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) + + 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_by_aircraft, fired_by) + if weapon_type_name == "FSWEAPON_FLARE": + buffer += pack("f", v_max) + + if with_size: + return pack("I",len(buffer))+buffer + + diff --git a/lib/YSplayer.py b/lib/YSplayer.py index 53c9092..e953aa0 100644 --- a/lib/YSplayer.py +++ b/lib/YSplayer.py @@ -5,9 +5,7 @@ class Player: self.username = username self.ip = ipAddr self.playerId = playerId - self.x = x - self.y = y - self.z = z + self.position = [x, y, z] self.throttle = throttle self.aam = aam self.agm = agm @@ -15,9 +13,7 @@ class Player: self.rktAmmo = rktAmmo self.fuel = fuel self.life = life - self.vx = vx - self.vy = vy - self.vz = vz + self.velocity = [vx, vy, vz] self.gValue = gValue self.streamWriterObject = streamWriterObject self.warningSent = warningSent @@ -25,7 +21,27 @@ class Player: def __str__(self): return f"Username: {self.username}, IP: {self.ip}, " \ - f"Player ID: {self.playerId}, X: {self.x}, Y: {self.y}, Z: {self.z}, " \ + f"Player ID: {self.playerId}, X: {self.getX()}, Y: {self.getY()}, Z: {self.getZ()}, " \ f"Throttle: {self.throttle}, AAM: {self.aam}, AGM: {self.agm}, " \ f"Gun Ammo: {self.gunAmmo}, Rocket Ammo: {self.rktAmmo}, Fuel: {self.fuel}" \ f"Life: {self.life}, gValue: {self.gValue}" + + def getX(self): + return self.position[0] + + def getY(self): + return self.position[1] + + def getZ(self): + return self.position[2] + + def setX(self, x): + self.position[0] = x + + def setY(self, y): + self.position[1] = y + + def setZ(self, z): + self.position[2] = z + + diff --git a/proxy.py b/proxy.py index 5852d67..b41355d 100644 --- a/proxy.py +++ b/proxy.py @@ -48,9 +48,9 @@ async def handle_client(client_reader, client_writer): if packet_type == 11: # Flight data packet playerData = parseFlightData(data) player.playerId = playerData[1] - player.x = playerData[2] - player.y = playerData[3] - player.z = playerData[4] + player.setX(playerData[2]) + player.setY(playerData[3]) + player.setZ(playerData[4]) player.throttle = playerData[22] player.aam = playerData[18] player.agm = playerData[19] -- cgit v1.2.3 From c6394820c3a9450c1000d4481d4a9b2e00f65f9c Mon Sep 17 00:00:00 2001 From: Skipper Date: Fri, 31 Jan 2025 21:28:04 +0000 Subject: Removing trailing whitespace --- lib/PacketManager.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/PacketManager.py b/lib/PacketManager.py index dcd1a8d..a07cf0d 100644 --- a/lib/PacketManager.py +++ b/lib/PacketManager.py @@ -652,9 +652,9 @@ class FSNETCMD_AIRPLANESTATE: #11 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]))) @@ -687,7 +687,7 @@ class FSNETCMD_AIRPLANESTATE: #11 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 @@ -699,7 +699,7 @@ class FSNETCMD_AIRPLANESTATE: #11 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 @@ -824,7 +824,7 @@ class FSNETCMD_EMPTYPACKET: self.buffer = buffer if should_decode: self.decode() - + def decode(self): pass # There are no messages in this packet! @@ -951,7 +951,7 @@ class FSNETCMD_MISSILELAUNCH: #20 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])) @@ -961,14 +961,14 @@ class FSNETCMD_MISSILELAUNCH: #20 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): @@ -986,8 +986,8 @@ class FSNETCMD_MISSILELAUNCH: #20 buffer += pack("II", fired_by_aircraft, fired_by) if weapon_type_name == "FSWEAPON_FLARE": buffer += pack("f", v_max) - + if with_size: return pack("I",len(buffer))+buffer - + return buffer -- cgit v1.2.3 From f40188672a49e1c6a85d0f5c6f47bda2fce75f1e Mon Sep 17 00:00:00 2001 From: Skipper Date: Sun, 2 Feb 2025 16:09:50 +0000 Subject: Refactored packets into separate files --- lib/PacketManager.py | 993 --------------------- lib/PacketManager/PacketManager.py | 39 + lib/PacketManager/__init__.py | 0 lib/PacketManager/packets/FSNETCMD_ADDOBJECT.py | 65 ++ lib/PacketManager/packets/FSNETCMD_AIRCMD.py | 53 ++ .../packets/FSNETCMD_AIRPLANESTATE.py | 250 ++++++ lib/PacketManager/packets/FSNETCMD_EMPTYPACKET.py | 19 + lib/PacketManager/packets/FSNETCMD_ENVIRONMENT.py | 51 ++ lib/PacketManager/packets/FSNETCMD_ERROR.py | 22 + lib/PacketManager/packets/FSNETCMD_GETDAMAGE.py | 43 + lib/PacketManager/packets/FSNETCMD_JOINAPPROVAL.py | 22 + lib/PacketManager/packets/FSNETCMD_JOINREQUEST.py | 29 + lib/PacketManager/packets/FSNETCMD_KILLSERVER.py | 14 + lib/PacketManager/packets/FSNETCMD_LOADFIELD.py | 28 + lib/PacketManager/packets/FSNETCMD_LOCKON.py | 31 + lib/PacketManager/packets/FSNETCMD_LOGOFF.py | 19 + lib/PacketManager/packets/FSNETCMD_LOGON.py | 42 + .../packets/FSNETCMD_MISSILELAUNCH.py | 64 ++ lib/PacketManager/packets/FSNETCMD_NULL.py | 16 + .../packets/FSNETCMD_PREPARESIMULATION.py | 14 + lib/PacketManager/packets/FSNETCMD_READBACK.py | 39 + .../packets/FSNETCMD_REJECTJOINREQ.py | 21 + .../packets/FSNETCMD_REMOVEAIRPLANE.py | 13 + lib/PacketManager/packets/FSNETCMD_REMOVEGROUND.py | 13 + lib/PacketManager/packets/FSNETCMD_REPORTSCORE.py | 50 ++ .../packets/FSNETCMD_REQUESTTESTAIRPLANE.py | 14 + .../packets/FSNETCMD_SERVER_FORCE_JOIN.py | 14 + lib/PacketManager/packets/FSNETCMD_SMOKECOLOR.py | 25 + lib/PacketManager/packets/FSNETCMD_TESTPACKET.py | 14 + lib/PacketManager/packets/FSNETCMD_TEXTMESSAGE.py | 24 + lib/PacketManager/packets/FSNETCMD_UNJOIN.py | 26 + lib/PacketManager/packets/__init__.py | 472 ++++++++++ 32 files changed, 1546 insertions(+), 993 deletions(-) delete mode 100644 lib/PacketManager.py create mode 100644 lib/PacketManager/PacketManager.py create mode 100644 lib/PacketManager/__init__.py create mode 100644 lib/PacketManager/packets/FSNETCMD_ADDOBJECT.py create mode 100644 lib/PacketManager/packets/FSNETCMD_AIRCMD.py create mode 100644 lib/PacketManager/packets/FSNETCMD_AIRPLANESTATE.py create mode 100644 lib/PacketManager/packets/FSNETCMD_EMPTYPACKET.py create mode 100644 lib/PacketManager/packets/FSNETCMD_ENVIRONMENT.py create mode 100644 lib/PacketManager/packets/FSNETCMD_ERROR.py create mode 100644 lib/PacketManager/packets/FSNETCMD_GETDAMAGE.py create mode 100644 lib/PacketManager/packets/FSNETCMD_JOINAPPROVAL.py create mode 100644 lib/PacketManager/packets/FSNETCMD_JOINREQUEST.py create mode 100644 lib/PacketManager/packets/FSNETCMD_KILLSERVER.py create mode 100644 lib/PacketManager/packets/FSNETCMD_LOADFIELD.py create mode 100644 lib/PacketManager/packets/FSNETCMD_LOCKON.py create mode 100644 lib/PacketManager/packets/FSNETCMD_LOGOFF.py create mode 100644 lib/PacketManager/packets/FSNETCMD_LOGON.py create mode 100644 lib/PacketManager/packets/FSNETCMD_MISSILELAUNCH.py create mode 100644 lib/PacketManager/packets/FSNETCMD_NULL.py create mode 100644 lib/PacketManager/packets/FSNETCMD_PREPARESIMULATION.py create mode 100644 lib/PacketManager/packets/FSNETCMD_READBACK.py create mode 100644 lib/PacketManager/packets/FSNETCMD_REJECTJOINREQ.py create mode 100644 lib/PacketManager/packets/FSNETCMD_REMOVEAIRPLANE.py create mode 100644 lib/PacketManager/packets/FSNETCMD_REMOVEGROUND.py create mode 100644 lib/PacketManager/packets/FSNETCMD_REPORTSCORE.py create mode 100644 lib/PacketManager/packets/FSNETCMD_REQUESTTESTAIRPLANE.py create mode 100644 lib/PacketManager/packets/FSNETCMD_SERVER_FORCE_JOIN.py create mode 100644 lib/PacketManager/packets/FSNETCMD_SMOKECOLOR.py create mode 100644 lib/PacketManager/packets/FSNETCMD_TESTPACKET.py create mode 100644 lib/PacketManager/packets/FSNETCMD_TEXTMESSAGE.py create mode 100644 lib/PacketManager/packets/FSNETCMD_UNJOIN.py create mode 100644 lib/PacketManager/packets/__init__.py (limited to 'lib') diff --git a/lib/PacketManager.py b/lib/PacketManager.py deleted file mode 100644 index a07cf0d..0000000 --- a/lib/PacketManager.py +++ /dev/null @@ -1,993 +0,0 @@ -from struct import unpack, pack -from math import pi - -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"] - -def decode(buffer:bytes, with_size:bool=False, should_decode=True): - """ - Takes incomming packets from client or server and decodes them. - If with_size is True, then the packet size is included in the buffer. - If decode is False, then the packet is just returned as is. - If it is true, then the packet will be decoded. - """ - - if with_size: - # Strips away the size, so we've just got the message. - buffer = buffer[4:] - - # Pull out the message type - msg_type = unpack("I", buffer[:4])[0] - - # Find message type on the message_types list - if msg_type < len(MESSAGE_TYPES): - msg_type = MESSAGE_TYPES[msg_type] - packet_class = None - if msg_type == "FSNETCMD_NULL": - packet_class = FSNETCMD_NULL - elif msg_type == "FSNETCMD_LOGON": - packet_class = FSNETCMD_LOGON - elif msg_type == "FSNETCMD_LOGOFF": - packet_class = FSNETCMD_LOGOFF - elif msg_type == "FSNETCMD_ERROR": - packet_class = FSNETCMD_ERROR - elif msg_type == "FSNETCMD_LOADFIELD": - packet_class = FSNETCMD_LOADFIELD - elif msg_type == "FSNETCMD_ADDOBJECT": - packet_class = FSNETCMD_ADDOBJECT - elif msg_type == "FSNETCMD_READBACK": - packet_class = FSNETCMD_READBACK - - if packet_class: - return packet_class(buffer, should_decode) - return None - - return None - - -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) - - -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 - - -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 - - -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 - - -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 - - -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 - - -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 - - -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 - - -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 - - -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 - - -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 - -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("fffhhhfff", *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 - - -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 - - -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 - - -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 - - -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 - - -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 - - -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 - - -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 - -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 - - -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 - - -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) - - 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_by_aircraft, fired_by) - if weapon_type_name == "FSWEAPON_FLARE": - buffer += pack("f", v_max) - - if with_size: - return pack("I",len(buffer))+buffer - return buffer - diff --git a/lib/PacketManager/PacketManager.py b/lib/PacketManager/PacketManager.py new file mode 100644 index 0000000..ea0d91b --- /dev/null +++ b/lib/PacketManager/PacketManager.py @@ -0,0 +1,39 @@ +from struct import unpack, pack +from PacketManager.packets import MESSAGE_TYPES, FSWEAPON_DICT, GUIDEDWEAPONS + + +class PackageManager: + + def __init__(self): + pass + + def decode(self, buffer:bytes, with_size:bool=False, should_decode=True): + """ + Takes incomming packets from client or server and decodes them. + If with_size is True, then the packet size is included in the buffer. + If decode is False, then the packet is just returned as is. + If it is true, then the packet will be decoded. + """ + + if with_size: + # Strips away the size, so we've just got the message. + buffer = buffer[4:] + + # Pull out the message type + msg_type = unpack("I", buffer[:4])[0] + + # Find message type on the message_types list + if msg_type < len(MESSAGE_TYPES): + msg_type = MESSAGE_TYPES[msg_type] + packet_class = None + try: + # Try to import the packet class + packet_class = __import__("PacketManager.packets."+msg_type, fromlist=[msg_type]) + except ImportError as e: + print(f"Error importing packet: {msg_type}") + print(e) + if packet_class: + return packet_class(buffer, should_decode) + return None + + return None diff --git a/lib/PacketManager/__init__.py b/lib/PacketManager/__init__.py new file mode 100644 index 0000000..e69de29 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 -- cgit v1.2.3 From f2189b417b543bea5add24f5a324b328fa8dbfac Mon Sep 17 00:00:00 2001 From: Skipper Date: Sun, 2 Feb 2025 21:32:14 +0000 Subject: Writing tests, and checking against existing packet decoders --- lib/PacketManager/PacketManager.py | 2 +- lib/PacketManager/packets/FSNETCMD_ADDOBJECT.py | 16 ++++---- lib/PacketManager/packets/FSNETCMD_JOINREQUEST.py | 2 +- lib/PacketManager/packets/FSNETCMD_LOADFIELD.py | 4 +- lib/PacketManager/packets/FSNETCMD_LOGON.py | 16 +++++++- lib/__init__.py | 0 tests/__init__.py | 0 tests/packet01_logon_test.py | 46 +++++++++++++++++++++++ tests/packet04_loadfield_test.py | 38 +++++++++++++++++++ tests/packet05_addobject_test.py | 39 +++++++++++++++++++ tests/packet08_joinrequest_test.py | 31 +++++++++++++++ tests/packet09_joinapproval_test.py | 26 +++++++++++++ 12 files changed, 208 insertions(+), 12 deletions(-) create mode 100644 lib/__init__.py create mode 100644 tests/__init__.py create mode 100644 tests/packet01_logon_test.py create mode 100644 tests/packet04_loadfield_test.py create mode 100644 tests/packet05_addobject_test.py create mode 100644 tests/packet08_joinrequest_test.py create mode 100644 tests/packet09_joinapproval_test.py (limited to 'lib') diff --git a/lib/PacketManager/PacketManager.py b/lib/PacketManager/PacketManager.py index ea0d91b..847d55c 100644 --- a/lib/PacketManager/PacketManager.py +++ b/lib/PacketManager/PacketManager.py @@ -2,7 +2,7 @@ from struct import unpack, pack from PacketManager.packets import MESSAGE_TYPES, FSWEAPON_DICT, GUIDEDWEAPONS -class PackageManager: +class PacketManager: def __init__(self): pass diff --git a/lib/PacketManager/packets/FSNETCMD_ADDOBJECT.py b/lib/PacketManager/packets/FSNETCMD_ADDOBJECT.py index f2e831e..ef7f785 100644 --- a/lib/PacketManager/packets/FSNETCMD_ADDOBJECT.py +++ b/lib/PacketManager/packets/FSNETCMD_ADDOBJECT.py @@ -26,15 +26,15 @@ class FSNETCMD_ADDOBJECT: #5 self.decode() def decode(self): - self.object_type, self.net_type = unpack("hh", self.buffer[4:8]) + 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.pos = list(unpack("fff", self.buffer[16:28])) + self.atti = list(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] @@ -52,10 +52,12 @@ class FSNETCMD_ADDOBJECT: #5 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) + buffer = pack('IHH', 5, object_type, net_type) + buffer += pack('IHHffffff', object_id, iff, 0, pos[0], pos[1], pos[2], atti[0], atti[1], atti[2]) + buffer += pack('32s32sI', identifier.encode(), substrname.encode(), ysfid) + buffer += pack('II', flags, flags0) + buffer += pack('f', outside_radius) + if aircraft_class and aircraft_category: buffer += pack("hhh", aircraft_class, aircraft_category, 0) if pilot: diff --git a/lib/PacketManager/packets/FSNETCMD_JOINREQUEST.py b/lib/PacketManager/packets/FSNETCMD_JOINREQUEST.py index 0df7287..7796599 100644 --- a/lib/PacketManager/packets/FSNETCMD_JOINREQUEST.py +++ b/lib/PacketManager/packets/FSNETCMD_JOINREQUEST.py @@ -23,7 +23,7 @@ class FSNETCMD_JOINREQUEST: #8 @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) + start_pos.encode(), 1, 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_LOADFIELD.py b/lib/PacketManager/packets/FSNETCMD_LOADFIELD.py index 2994591..2184f7d 100644 --- a/lib/PacketManager/packets/FSNETCMD_LOADFIELD.py +++ b/lib/PacketManager/packets/FSNETCMD_LOADFIELD.py @@ -21,7 +21,9 @@ class FSNETCMD_LOADFIELD: #4 @staticmethod def encode(field, flags, pos, atti, with_size:bool=False): - buffer = pack("I32sIffffff", 4, field.encode(), flags, pos[0], pos[1], + if isinstance(field, str): + field = field.encode() + buffer = pack("I32sIffffff", 4, field, flags, pos[0], pos[1], pos[2], atti[0], atti[1], atti[2]) if with_size: return pack("I",len(buffer))+buffer diff --git a/lib/PacketManager/packets/FSNETCMD_LOGON.py b/lib/PacketManager/packets/FSNETCMD_LOGON.py index 84d6c86..4787e63 100644 --- a/lib/PacketManager/packets/FSNETCMD_LOGON.py +++ b/lib/PacketManager/packets/FSNETCMD_LOGON.py @@ -23,6 +23,11 @@ class FSNETCMD_LOGON: #1 self.alias = self.buffer[24:].decode().strip('\x00') else: self.alias = self.username + if isinstance(self.username,bytes): + self.username = self.username.decode().strip('\x00') + if isinstance(self.alias,bytes): + self.alias = self.alias.decode().strip('\x00') + @staticmethod #Method to create a logon packet, if required. def encode(username, version, with_size:bool=False): @@ -32,9 +37,16 @@ class FSNETCMD_LOGON: #1 else: shortform = username alias = None - buffer = pack("I16sI", 1, shortform.encode(), version) + if isinstance(shortform,str): + shortform = shortform.encode() + buffer = pack("I16sI", 1, shortform, version) if alias: - buffer += alias.encode() + if isinstance(alias,str): + alias = alias.encode() + if len(alias)<200: + alias = alias.ljust(200,b'\x00') + alias += b'\x00\x00\x00\x00' + buffer += alias if with_size: return pack("I",len(buffer))+buffer diff --git a/lib/__init__.py b/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/packet01_logon_test.py b/tests/packet01_logon_test.py new file mode 100644 index 0000000..9650585 --- /dev/null +++ b/tests/packet01_logon_test.py @@ -0,0 +1,46 @@ +import unittest +import sys +import os + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +from lib.PacketManager.packets.FSNETCMD_LOGON import FSNETCMD_LOGON + +class TestFSNETCMD_LOGON(unittest.TestCase): + + def test_decode(self): + buffer = b'\x01\x00\x00\x00User1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x99x3\x01' + packet = FSNETCMD_LOGON(buffer) + self.assertEqual(packet.username, 'User1') + self.assertEqual(packet.version, 20150425) + self.assertEqual(packet.alias, 'User1') + + def test_decode_with_alias(self): + buffer = b'\x01\x00\x00\x00ExtraLongUserna\x00\x99x3\x01ExtraLongUsernameThatIsTooLongForYS\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + packet = FSNETCMD_LOGON(buffer) + self.assertEqual(packet.username, 'ExtraLongUserna') + self.assertEqual(packet.version, 20150425) + self.assertEqual(packet.alias, 'ExtraLongUsernameThatIsTooLongForYS') + + def test_encode(self): + username = "User1" + version = 20150425 + buffer = FSNETCMD_LOGON.encode(username, version) + expected_buffer = b'\x01\x00\x00\x00User1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x99x3\x01' + self.assertEqual(buffer, expected_buffer) + + def test_encode_with_alias(self): + username = "ExtraLongUsernameThatIsTooLongForYS" + version = 20150425 + buffer = FSNETCMD_LOGON.encode(username, version) + expected_buffer = b'\x01\x00\x00\x00ExtraLongUserna\x00\x99x3\x01ExtraLongUsernameThatIsTooLongForYS\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + self.assertEqual(buffer, expected_buffer) + + def test_encode_with_size(self): + username = "User1" + version = 20150425 + buffer = FSNETCMD_LOGON.encode(username, version, with_size=True) + expected_buffer = b'\x18\x00\x00\x00\x01\x00\x00\x00User1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x99x3\x01' + self.assertEqual(buffer, expected_buffer) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/tests/packet04_loadfield_test.py b/tests/packet04_loadfield_test.py new file mode 100644 index 0000000..a9004be --- /dev/null +++ b/tests/packet04_loadfield_test.py @@ -0,0 +1,38 @@ +import unittest +import sys +import os + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +from lib.PacketManager.packets.FSNETCMD_LOADFIELD import FSNETCMD_LOADFIELD +from struct import pack + +class TestFSNETCMD_LOADFIELD(unittest.TestCase): + + def test_decode(self): + buffer = b'\x04\x00\x00\x00ISLAND_GOURD\x00\xc39\x00\xe0\x1ff\x00\r\x00\x00\x00\r\x00\x00\x00\x10\xc49\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + packet = FSNETCMD_LOADFIELD(buffer) + self.assertEqual(packet.fieldShortName, 'ISLAND_GOURD') + self.assertEqual(packet.flags, 0) + self.assertEqual(packet.pos, [0, 0, 0]) + self.assertEqual(packet.atti, [0, 0,0]) + + def test_encode(self): + field = b'ISLAND_GOURD\x00\xc39\x00\xe0\x1ff\x00\r\x00\x00\x00\r\x00\x00\x00\x10\xc49\x00' + flags = 0 + pos = [0, 0, 0] + atti = [0, 0, 0] + buffer = FSNETCMD_LOADFIELD.encode(field, flags, pos, atti) + expected_buffer = b'\x04\x00\x00\x00ISLAND_GOURD\x00\xc39\x00\xe0\x1ff\x00\r\x00\x00\x00\r\x00\x00\x00\x10\xc49\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + self.assertEqual(buffer, expected_buffer) + + def test_encode_with_size(self): + field = b'ISLAND_GOURD\x00\xc39\x00\xe0\x1ff\x00\r\x00\x00\x00\r\x00\x00\x00\x10\xc49\x00' + flags = 0 + pos = [0, 0, 0] + atti = [0, 0, 0] + buffer = FSNETCMD_LOADFIELD.encode(field, flags, pos, atti, with_size=True) + expected_buffer = b'@\x00\x00\x00\x04\x00\x00\x00ISLAND_GOURD\x00\xc39\x00\xe0\x1ff\x00\r\x00\x00\x00\r\x00\x00\x00\x10\xc49\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + self.assertEqual(buffer, expected_buffer) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/tests/packet05_addobject_test.py b/tests/packet05_addobject_test.py new file mode 100644 index 0000000..6378858 --- /dev/null +++ b/tests/packet05_addobject_test.py @@ -0,0 +1,39 @@ +import unittest +import sys +import os + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +from lib.PacketManager.packets.FSNETCMD_ADDOBJECT import FSNETCMD_ADDOBJECT + +class TestFSNETCMD_ADDOBJECT(unittest.TestCase): + + def test_decode(self): + buffer = b'\x05\x00\x00\x00\x01\x00\x01\x00\x02\x00\x01\x00\x00\x00\x00\x00R\x0c^E\x00\x00\x00\x00H\x85%E\x1d\x96\xcb\xbf\x00\x00\x00\x00\x00\x00\x00\x80HANGER\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\xd7\xa3dB' + + packet = FSNETCMD_ADDOBJECT(buffer) + self.assertEqual(packet.object_type, 1) + self.assertEqual(packet.net_type, 1) + self.assertEqual(packet.object_id, 65538) + self.assertEqual(packet.iff, 0) + self.assertEqual(packet.pos, [3552.77001953125, 0.0, 2648.330078125]) + self.assertEqual(packet.atti, [-1.590518593788147, 0.0, -0.0]) + self.assertEqual(packet.identifier, 'HANGER') + self.assertEqual(packet.substrname, '') + self.assertEqual(packet.ysfid, 0) + self.assertEqual(packet.flags, 0) + self.assertEqual(packet.flags0, 32768) + self.assertEqual(packet.outsideRadius, 57.15999984741211) + + def test_encode(self): + buffer = FSNETCMD_ADDOBJECT.encode( + object_type=1, net_type=1, object_id=65538, iff=0, pos=[3552.77001953125, 0.0, 2648.330078125], + atti=[-1.590518593788147, 0.0, -0.0], identifier='HANGER', substrname='', ysfid=0, + flags=0, flags0=32768, outside_radius=57.15999984741211 + ) + expected_buffer = b'\x05\x00\x00\x00\x01\x00\x01\x00\x02\x00\x01\x00\x00\x00\x00\x00R\x0c^E\x00\x00\x00\x00H\x85%E\x1d\x96\xcb\xbf\x00\x00\x00\x00\x00\x00\x00\x80HANGER\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\xd7\xa3dB' + self.assertEqual(buffer, expected_buffer) + + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/tests/packet08_joinrequest_test.py b/tests/packet08_joinrequest_test.py new file mode 100644 index 0000000..e712989 --- /dev/null +++ b/tests/packet08_joinrequest_test.py @@ -0,0 +1,31 @@ +import unittest +import sys +import os + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +from lib.PacketManager.packets.FSNETCMD_JOINREQUEST import FSNETCMD_JOINREQUEST + +class TestFSNETCMD_JOINREQUEST(unittest.TestCase): + + def test_decode(self): + buffer = b'\x08\x00\x00\x00\x02\x00\x00\x00EUROFIGHTER_TYPHOON\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00RW36_01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00K\x00\x00\x00' + packet = FSNETCMD_JOINREQUEST(buffer) + self.assertEqual(packet.iff,2) + self.assertEqual(packet.aircraft, 'EUROFIGHTER_TYPHOON') + self.assertEqual(packet.start_pos, 'RW36_01') + self.assertEqual(packet.fuel, 75) + self.assertEqual(packet.smoke,0) + + def test_encode(self): + iff = 2 + aircraft = 'EUROFIGHTER_TYPHOON' + start_pos = 'NORTH10000_01' + fuel = 75 + smoke = 0 + buffer = FSNETCMD_JOINREQUEST.encode(iff, aircraft, start_pos, fuel, smoke) + expected_buffer = b'\x08\x00\x00\x00\x02\x00\x00\x00EUROFIGHTER_TYPHOON\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00NORTH10000_01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00K\x00\x00\x00' + self.assertEqual(buffer, expected_buffer) + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/tests/packet09_joinapproval_test.py b/tests/packet09_joinapproval_test.py new file mode 100644 index 0000000..6334f78 --- /dev/null +++ b/tests/packet09_joinapproval_test.py @@ -0,0 +1,26 @@ +import unittest +import sys +import os + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +from lib.PacketManager.packets.FSNETCMD_JOINAPPROVAL import FSNETCMD_JOINAPPROVAL + +class TestFSNETCMD_JOINAPPROVAL(unittest.TestCase): + + def test_decode(self): + buffer = b'\t\x00\x00\x00' + packet = FSNETCMD_JOINAPPROVAL(buffer) + self.assertEqual(packet.buffer, buffer) + + def test_encode(self): + buffer = FSNETCMD_JOINAPPROVAL.encode() + expected_buffer = b'\t\x00\x00\x00' + self.assertEqual(buffer, expected_buffer) + + def test_encode_with_size(self): + buffer = FSNETCMD_JOINAPPROVAL.encode(with_size=True) + expected_buffer = b'\x04\x00\x00\x00\t\x00\x00\x00' + self.assertEqual(buffer, expected_buffer) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file -- cgit v1.2.3 From 0b53c6b864735dbc0ebf675eaa478f1275ae0fa9 Mon Sep 17 00:00:00 2001 From: Skipper Date: Mon, 3 Feb 2025 11:19:02 +0000 Subject: Added discord client Begun implementation of the discord client. Will exclude the config.py after this - so any changes made during testing (including API keys) will not be copied through - un-ignore if adding new config values. --- config.py | 9 +++++++++ lib/DiscordClient.py | 42 ++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + testDiscord.py | 7 +++++++ 4 files changed, 59 insertions(+) create mode 100644 lib/DiscordClient.py create mode 100644 requirements.txt create mode 100644 testDiscord.py (limited to 'lib') diff --git a/config.py b/config.py index ab3b14c..c8cce89 100644 --- a/config.py +++ b/config.py @@ -44,3 +44,12 @@ SMOKE_PLANE = True # Planes smoking minimum life SMOKE_LIFE = 5 + + +#Discord chat integration +#Make sure to enable read message privilege for the bot. + +DISCORD_TOKEN = "YourDiscordBotTOKEN" + +# Channel ID for the chat +CHANNEL_ID = 0 # Channel ID, as an integer diff --git a/lib/DiscordClient.py b/lib/DiscordClient.py new file mode 100644 index 0000000..431a1d1 --- /dev/null +++ b/lib/DiscordClient.py @@ -0,0 +1,42 @@ +import discord +import asyncio +from lib.PacketManager.packets.FSNETCMD_TEXTMESSAGE import FSNETCMD_TEXTMESSAGE +from config import DISCORD_TOKEN, CHANNEL_ID + +class DiscordClient(discord.Client): + + def __init__(self, intents, parent=None): + super().__init__(intents=intents) + self.parent = parent + + async def on_ready(self): + await self.check_messages() + + async def on_message(self, message): + if message.channel.id == CHANNEL_ID: + if message.author.id == self.user.id: + return + author = message.author.name + content = message.content + messageToSendToClients = FSNETCMD_TEXTMESSAGE.encode(author, content) + if self.parent: + self.parent.send_to_all(messageToSendToClients) + + async def send_message(self, message): + channel = self.get_channel(CHANNEL_ID) + if channel: + message = await channel.send(message) + + async def check_messages(self): + while True: + if self.parent: + for message in self.parent.chatLog: + await self.send_message(message) + self.parent.chatLog.remove(message) + await asyncio.sleep(1) + + def start_bot(self): + loop = asyncio.get_event_loop() + loop.create_task(self.check_messages()) + self.run(DISCORD_TOKEN) + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..844f49a --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +discord.py diff --git a/testDiscord.py b/testDiscord.py new file mode 100644 index 0000000..ca084e8 --- /dev/null +++ b/testDiscord.py @@ -0,0 +1,7 @@ +from lib.DiscordClient import DiscordClient +import discord + +intents = discord.Intents.default() +intents.message_content = True +client = DiscordClient(intents) +client.start_bot() -- cgit v1.2.3 From 85d07549622d37e8c133bfc9482f35f3f4b30239 Mon Sep 17 00:00:00 2001 From: Skipper Date: Mon, 3 Feb 2025 21:45:42 +0000 Subject: Added weapon config and version update --- lib/PacketManager/packets/FSNETCMD_LOGON.py | 6 ++- lib/PacketManager/packets/FSNETCMD_WEAPONCONFIG.py | 55 ++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 lib/PacketManager/packets/FSNETCMD_WEAPONCONFIG.py (limited to 'lib') diff --git a/lib/PacketManager/packets/FSNETCMD_LOGON.py b/lib/PacketManager/packets/FSNETCMD_LOGON.py index 4787e63..9848590 100644 --- a/lib/PacketManager/packets/FSNETCMD_LOGON.py +++ b/lib/PacketManager/packets/FSNETCMD_LOGON.py @@ -51,4 +51,8 @@ class FSNETCMD_LOGON: #1 if with_size: return pack("I",len(buffer))+buffer - return buffer \ No newline at end of file + return buffer + + @staticmethod + def alter_version(buffer:bytes, new_version:int): + return buffer[:20]+pack("I",new_version)+buffer[24:] \ No newline at end of file diff --git a/lib/PacketManager/packets/FSNETCMD_WEAPONCONFIG.py b/lib/PacketManager/packets/FSNETCMD_WEAPONCONFIG.py new file mode 100644 index 0000000..1ea24e4 --- /dev/null +++ b/lib/PacketManager/packets/FSNETCMD_WEAPONCONFIG.py @@ -0,0 +1,55 @@ +from struct import pack, unpack +from . import FSWEAPON_DICT + +class FSNETCMD_WEAPONCONFIG: + def __init__(self, buffer:bytes, should_decode:bool=True): + self.buffer = buffer + self.aircraft_id = None + self.number = None + self.weapon_config = {} + + + if should_decode: + self.decode() + + def decode(self): + self.aircraft_id, self.number = unpack("Ih",self.buffer[4:10]) + self.weapon_config = {} + self.number = self.number & (~1) + + for i in range(self.number // 2): + typ, count = unpack("hh",self.buffer[10+i*4:14+i*4]) + if typ in FSWEAPON_DICT: + typ = FSWEAPON_DICT[typ] + if "SMOKE" in typ: + #the count is actually the RGB value, formatted like a ballbag. + r = (count >>10)&31 + g = (count >>5)&31 + b = count&31 + r = (r>>2)+(r<<3) + g = (g>>2)+(g<<3) + b = (b>>2)+(b<<3) + count = [r,g,b] + self.weapon_config[typ] = count + print(self.weapon_config) + + @staticmethod + def encode(aircraft_id:int, weapon_config:dict, with_size:bool=False): + buffer = pack("IIh", 36, aircraft_id, len(weapon_config)*2) + for typ, count in weapon_config.items(): + if typ in FSWEAPON_DICT.values() and isinstance(typ, str): + typ = [k for k,v in FSWEAPON_DICT.items() if v == typ][0] + if typ >=32 and typ <=39 and isinstance(count,list): #Smoke + r,g,b = count + r = (r*31)/255 + g = (g*31)/255 + b = (b*31)/255 + count = (int(r)<<10)+(int(g)<<5)+int(b) + buffer += pack("hh", typ, count) + if with_size: + return pack("I",len(buffer))+buffer + return buffer + + @staticmethod + def addSmoke(aircraft_id:int): + return FSNETCMD_WEAPONCONFIG.encode(aircraft_id, {32:[66,66,66]}, True) -- cgit v1.2.3 From 313338fc498c089da5860ba6b5b0673be848c45b Mon Sep 17 00:00:00 2001 From: Skipper Date: Tue, 4 Feb 2025 22:14:40 +0000 Subject: Updated some packets --- lib/PacketManager/PacketManager.py | 33 +- lib/PacketManager/packets/FSNETCMD_AIRCMD.py | 2 +- .../packets/FSNETCMD_AIRPLANESTATE.py | 72 +-- lib/PacketManager/packets/FSNETCMD_ERROR.py | 2 +- lib/PacketManager/packets/FSNETCMD_GETDAMAGE.py | 2 +- .../packets/FSNETCMD_MISSILELAUNCH.py | 2 +- lib/PacketManager/packets/FSNETCMD_WEAPONCONFIG.py | 2 +- lib/PacketManager/packets/__init__.py | 538 +++------------------ lib/PacketManager/packets/constants.py | 472 ++++++++++++++++++ lib/YSchat.py | 2 + proxy.py | 80 +-- 11 files changed, 633 insertions(+), 574 deletions(-) create mode 100644 lib/PacketManager/packets/constants.py (limited to 'lib') diff --git a/lib/PacketManager/PacketManager.py b/lib/PacketManager/PacketManager.py index 847d55c..f019369 100644 --- a/lib/PacketManager/PacketManager.py +++ b/lib/PacketManager/PacketManager.py @@ -7,33 +7,6 @@ class PacketManager: def __init__(self): pass - def decode(self, buffer:bytes, with_size:bool=False, should_decode=True): - """ - Takes incomming packets from client or server and decodes them. - If with_size is True, then the packet size is included in the buffer. - If decode is False, then the packet is just returned as is. - If it is true, then the packet will be decoded. - """ - - if with_size: - # Strips away the size, so we've just got the message. - buffer = buffer[4:] - - # Pull out the message type - msg_type = unpack("I", buffer[:4])[0] - - # Find message type on the message_types list - if msg_type < len(MESSAGE_TYPES): - msg_type = MESSAGE_TYPES[msg_type] - packet_class = None - try: - # Try to import the packet class - packet_class = __import__("PacketManager.packets."+msg_type, fromlist=[msg_type]) - except ImportError as e: - print(f"Error importing packet: {msg_type}") - print(e) - if packet_class: - return packet_class(buffer, should_decode) - return None - - return None + def get_packet_type(self, data: bytes): + #Returns the message type of that packet. + return MESSAGE_TYPES[unpack("> 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] + c = unpack("B", self.buffer[55:56])[0] self.flap = (c >> 4 & 15) / 15.0 self.brake = (c & 15) / 15.0 - flags = unpack("h", self.buffer[60:62])[0] + flags = unpack("h", self.buffer[56:58])[0] self.flags["ab"] = bool(flags & 1) # Last bit self.flags["firing"] = bool(flags &8) self.flags["smoke"] = 0 @@ -106,42 +112,42 @@ class FSNETCMD_AIRPLANESTATE: #11 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.gun_ammo, self.rocket_ammo, self.aam, self.agm, self.bomb = unpack("HHBBB", self.buffer[58:65]) + self.life = unpack("B", self.buffer[65:66])[0] - self.g_value = unpack("b", self.buffer[69:70])[0]/10.0 + self.g_value = unpack("b", self.buffer[66:67])[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 + self.throttle = unpack("B", self.buffer[67:68])[0]/99.0 + self.elev = unpack("b", self.buffer[68:69])[0]/99.0 + self.ail = unpack("b", self.buffer[69:70])[0]/99.0 + self.rud = unpack("b", self.buffer[70:71])[0]/99.0 + self.trim = unpack("b", self.buffer[71:72])[0]/99.0 if self.packet_version == 4: - c = unpack("B", self.buffer[75:76])[0] + c = unpack("B", self.buffer[71:72])[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] + c = unpack("B", self.buffer[72:73])[0] self.bomb_bay_info = (c >> 4 & 15) / 15.0 else: - self.position = list(unpack("fff", self.buffer[20:32])) + self.position = list(unpack("fff", self.buffer[16:28])) self.atti = list(map(lambda x: x / (pi / 32768.0), - unpack("hhh", self.buffer[32:38]))) + unpack("hhh", self.buffer[28:34]))) self.velocity = list(map(lambda x: x/10, - unpack("hhh", self.buffer[38:44]))) + unpack("hhh", self.buffer[34:40]))) 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]) + unpack("hhh", self.buffer[40:46]))) + self.g_value = unpack("h", self.buffer[46:48])[0]/100.0 + self.gun_ammo, self.aam, self.agm, self.bomb, self.smoke_oil = unpack("hhhhh", self.buffer[48:58]) + self.fuel = unpack("f", self.buffer[58:62])[0] self.payload = unpack("f", self.buffer[62:66])[0] - self.life = unpack("H", self.buffer[66:68])[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 @@ -168,6 +174,7 @@ class FSNETCMD_AIRPLANESTATE: #11 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]))) @@ -176,6 +183,15 @@ class FSNETCMD_AIRPLANESTATE: #11 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 get_life(buffer:bytes): + version = unpack("h", buffer[12:14])[0] + if version == 5 or version == 4: + return unpack("B", buffer[65:66])[0] + else: + return unpack("H", buffer[66:68])[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, diff --git a/lib/PacketManager/packets/FSNETCMD_ERROR.py b/lib/PacketManager/packets/FSNETCMD_ERROR.py index 874ca51..327d38c 100644 --- a/lib/PacketManager/packets/FSNETCMD_ERROR.py +++ b/lib/PacketManager/packets/FSNETCMD_ERROR.py @@ -1,5 +1,5 @@ from struct import pack, unpack -from . import ERROR_CODES +from .constants import ERROR_CODES class FSNETCMD_ERROR: #3 def __init__(self, buffer:bytes, should_decode:bool=True): diff --git a/lib/PacketManager/packets/FSNETCMD_GETDAMAGE.py b/lib/PacketManager/packets/FSNETCMD_GETDAMAGE.py index e7988a6..1becb40 100644 --- a/lib/PacketManager/packets/FSNETCMD_GETDAMAGE.py +++ b/lib/PacketManager/packets/FSNETCMD_GETDAMAGE.py @@ -1,5 +1,5 @@ from struct import pack, unpack -from . import FSWEAPON_DICT +from .constants import FSWEAPON_DICT class FSNETCMD_GETDAMAGE: #22 """ diff --git a/lib/PacketManager/packets/FSNETCMD_MISSILELAUNCH.py b/lib/PacketManager/packets/FSNETCMD_MISSILELAUNCH.py index 7d7000a..4059773 100644 --- a/lib/PacketManager/packets/FSNETCMD_MISSILELAUNCH.py +++ b/lib/PacketManager/packets/FSNETCMD_MISSILELAUNCH.py @@ -1,5 +1,5 @@ from struct import pack, unpack -from . import FSWEAPON_DICT, GUIDEDWEAPONS +from .constants import FSWEAPON_DICT, GUIDEDWEAPONS class FSNETCMD_MISSILELAUNCH: #20 """ diff --git a/lib/PacketManager/packets/FSNETCMD_WEAPONCONFIG.py b/lib/PacketManager/packets/FSNETCMD_WEAPONCONFIG.py index 1ea24e4..b6acf82 100644 --- a/lib/PacketManager/packets/FSNETCMD_WEAPONCONFIG.py +++ b/lib/PacketManager/packets/FSNETCMD_WEAPONCONFIG.py @@ -1,5 +1,5 @@ from struct import pack, unpack -from . import FSWEAPON_DICT +from .constants import FSWEAPON_DICT class FSNETCMD_WEAPONCONFIG: def __init__(self, buffer:bytes, should_decode:bool=True): diff --git a/lib/PacketManager/packets/__init__.py b/lib/PacketManager/packets/__init__.py index 007c6b4..43abcf0 100644 --- a/lib/PacketManager/packets/__init__.py +++ b/lib/PacketManager/packets/__init__.py @@ -1,472 +1,66 @@ -#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 +from .FSNETCMD_LOGON import FSNETCMD_LOGON +from .FSNETCMD_LOGOFF import FSNETCMD_LOGOFF +from .FSNETCMD_ERROR import FSNETCMD_ERROR +from .FSNETCMD_LOADFIELD import FSNETCMD_LOADFIELD +from .FSNETCMD_ADDOBJECT import FSNETCMD_ADDOBJECT +from .FSNETCMD_READBACK import FSNETCMD_READBACK +from .FSNETCMD_SMOKECOLOR import FSNETCMD_SMOKECOLOR +from .FSNETCMD_JOINREQUEST import FSNETCMD_JOINREQUEST +from .FSNETCMD_JOINAPPROVAL import FSNETCMD_JOINAPPROVAL +from .FSNETCMD_REJECTJOINREQ import FSNETCMD_REJECTJOINREQ +from .FSNETCMD_AIRPLANESTATE import FSNETCMD_AIRPLANESTATE +from .FSNETCMD_UNJOIN import FSNETCMD_UNJOIN +from .FSNETCMD_REMOVEAIRPLANE import FSNETCMD_REMOVEAIRPLANE +from .FSNETCMD_REQUESTTESTAIRPLANE import FSNETCMD_REQUESTTESTAIRPLANE +from .FSNETCMD_KILLSERVER import FSNETCMD_KILLSERVER +from .FSNETCMD_PREPARESIMULATION import FSNETCMD_PREPARESIMULATION +from .FSNETCMD_TESTPACKET import FSNETCMD_TESTPACKET +from .FSNETCMD_LOCKON import FSNETCMD_LOCKON +from .FSNETCMD_REMOVEGROUND import FSNETCMD_REMOVEGROUND +from .FSNETCMD_MISSILELAUNCH import FSNETCMD_MISSILELAUNCH +#from .FSNETCMD_GROUNDSTATE import FSNETCMD_GROUNDSTATE +from .FSNETCMD_GETDAMAGE import FSNETCMD_GETDAMAGE +#from .FSNETCMD_GNDTURRETSTATE import FSNETCMD_GNDTURRETSTATE +#from .FSNETCMD_SETTESTAUTOPILOT import FSNETCMD_SETTESTAUTOPILOT +#from .FSNETCMD_REQTOBESIDEWINDOWOFSVR import FSNETCMD_REQTOBESIDEWINDOWOFSVR +#from .FSNETCMD_ASSIGNSIDEWINDOW import FSNETCMD_ASSIGNSIDEWINDOW +#from .FSNETCMD_RESENDAIRREQUEST import FSNETCMD_RESENDAIRREQUEST +#from .FSNETCMD_RESENDGNDREQUEST import FSNETCMD_RESENDGNDREQUEST +#from .FSNETCMD_VERSIONNOTIFY import FSNETCMD_VERSIONNOTIFY +from .FSNETCMD_AIRCMD import FSNETCMD_AIRCMD +#from .FSNETCMD_USEMISSILE import FSNETCMD_USEMISSILE +from .FSNETCMD_TEXTMESSAGE import FSNETCMD_TEXTMESSAGE +from .FSNETCMD_ENVIRONMENT import FSNETCMD_ENVIRONMENT +#from .FSNETCMD_NEEDRESENDJOINAPPROVAL import FSNETCMD_NEEDRESENDJOINAPPROVAL +#from .FSNETCMD_REVIVEGROUND import FSNETCMD_REVIVEGROUND +from .FSNETCMD_WEAPONCONFIG import FSNETCMD_WEAPONCONFIG +#from .FSNETCMD_LISTUSER import FSNETCMD_LISTUSER +#from .FSNETCMD_QUERYAIRSTATE import FSNETCMD_QUERYAIRSTATE +#from .FSNETCMD_USEUNGUIDEDWEAPON import FSNETCMD_USEUNGUIDEDWEAPON +#from .FSNETCMD_AIRTURRETSTATE import FSNETCMD_AIRTURRETSTATE +#from .FSNETCMD_CTRLSHOWUSERNAME import FSNETCMD_CTRLSHOWUSERNAME +#from .FSNETCMD_CONFIRMEXISTENCE import FSNETCMD_CONFIRMEXISTENCE +#from .FSNETCMD_CONFIGSTRING import FSNETCMD_CONFIGSTRING +#from .FSNETCMD_LIST import FSNETCMD_LIST +#from .FSNETCMD_GNDCMD import FSNETCMD_GNDCMD +#from .FSNETCMD_REPORTSCORE import FSNETCMD_REPORTSCORE +#from .FSNETCMD_SERVER_FORCE_JOIN import FSNETCMD_SERVER_FORCE_JOIN +#from .FSNETCMD_FOGCOLOR import FSNETCMD_FOGCOLOR +#from .FSNETCMD_SKYCOLOR import FSNETCMD_SKYCOLOR +#from .FSNETCMD_GNDCOLOR import FSNETCMD_GNDCOLOR +#from .FSNETCMD_RESERVED_FOR_LIGHTCOLOR import FSNETCMD_RESERVED_FOR_LIGHTCOLOR +#from .FSNETCMD_GENERATEATTACKER import FSNETCMD_GENERATEATTACKER + + +__all__ = ["FSNETCMD_LOGON", "FSNETCMD_LOGOFF", "FSNETCMD_ERROR", + "FSNETCMD_LOADFIELD", "FSNETCMD_ADDOBJECT", + "FSNETCMD_READBACK", "FSNETCMD_SMOKECOLOR", + "FSNETCMD_JOINREQUEST", "FSNETCMD_JOINAPPROVAL", + "FSNETCMD_REJECTJOINREQ", "FSNETCMD_AIRPLANESTATE", + "FSNETCMD_UNJOIN", "FSNETCMD_REMOVEAIRPLANE", + "FSNETCMD_REQUESTTESTAIRPLANE", "FSNETCMD_KILLSERVER", + "FSNETCMD_PREPARESIMULATION", "FSNETCMD_TESTPACKET", + "FSNETCMD_LOCKON", "FSNETCMD_REMOVEGROUND", + "FSNETCMD_MISSILELAUNCH", "FSNETCMD_GETDAMAGE", + "FSNETCMD_WEAPONCONFIG", "FSNETCMD_AIRCMD", + "FSNETCMD_TEXTMESSAGE", "FSNETCMD_ENVIRONMENT"] diff --git a/lib/PacketManager/packets/constants.py b/lib/PacketManager/packets/constants.py new file mode 100644 index 0000000..7a32e22 --- /dev/null +++ b/lib/PacketManager/packets/constants.py @@ -0,0 +1,472 @@ + + +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 +] diff --git a/lib/YSchat.py b/lib/YSchat.py index fb23208..d3db201 100644 --- a/lib/YSchat.py +++ b/lib/YSchat.py @@ -1,4 +1,6 @@ from struct import pack, unpack +from lib.PacketManager.packets import FSNETCMD_TEXTMESSAGE +#Re-write this to wrap the FSNETCMD_TEXTMESSAGE class def send(buffer: bytes): """ diff --git a/proxy.py b/proxy.py index b41355d..730a9b3 100644 --- a/proxy.py +++ b/proxy.py @@ -7,9 +7,12 @@ import asyncio from struct import unpack, pack from lib.parseFlightData import parseFlightData from lib import YSchat, YSplayer, YSendFlight, YSundead, YSviaversion +from lib.PacketManager.PacketManager import PacketManager +from lib.PacketManager.packets import FSNETCMD_AIRPLANESTATE import logging from logging import critical, warning, info, debug from config import * +from time import sleep # Configuration SERVER_HOST = SERVER_HOST @@ -37,20 +40,41 @@ async def handle_client(client_reader, client_writer): async def forward(reader, writer, direction, player=player): while True: try: - data = await reader.read(4096) - if not data: + header = await reader.readexactly(4) # Ensures we always get 4 bytes + if not header: + break # Connection closed + + length = unpack("I", header)[0] + + packet = await reader.read(length) + + if not packet: break + + data = header + packet + if direction == "client_to_server": try: + packet_type = PacketManager().get_packet_type(data) + if packet_type == "FSNETCMD_AIRPLANESTATE": + life = FSNETCMD_AIRPLANESTATE.get_life(data) + if player.life == -1: #Uninitialised + player.life = life + elif life > player.life: + cheatingMsg = YSchat.message(f"{HEALTH_HACK_MESSAGE} by {player.username}") + writer.write(cheatingMsg) + await writer.drain() + player.life = life + length, packet_type = unpack(" Date: Mon, 10 Feb 2025 12:47:24 +0000 Subject: Some updates to packets Various updates, added smoke function to airplanestate, which modifies the incoming packet to make the client smoke, needs to be taken with WEAPONCONFIG.addSmoke Added in placeholder classes for Player and Aircraft --- config.py | 4 +- lib/Aircraft.py | 83 ++++++ lib/PacketManager/PacketManager.py | 4 +- .../packets/FSNETCMD_AIRPLANESTATE.py | 55 +++- .../packets/FSNETCMD_MISSILELAUNCH.py | 57 +++- lib/PacketManager/packets/FSNETCMD_TEXTMESSAGE.py | 2 +- lib/PacketManager/packets/FSNETCMD_WEAPONCONFIG.py | 2 +- lib/Player.py | 35 +++ proxy.py | 307 ++++++++++++--------- 9 files changed, 393 insertions(+), 156 deletions(-) create mode 100644 lib/Aircraft.py create mode 100644 lib/Player.py (limited to 'lib') diff --git a/config.py b/config.py index c8cce89..e9fb1fd 100644 --- a/config.py +++ b/config.py @@ -8,9 +8,9 @@ LOGGING_LEVEL = INFO # Server Configuration # Replace with the YSFlight server address SERVER_HOST = "127.0.0.1" -SERVER_PORT = 7915 # Please put where the normal YSFlight server is running +SERVER_PORT = 7914 # Please put where the normal YSFlight server is running # Port for the proxy server -PROXY_PORT = 9000 +PROXY_PORT = 7915 # Native YSFlight Server # Please select the YSFlight server version for the diff --git a/lib/Aircraft.py b/lib/Aircraft.py new file mode 100644 index 0000000..3b79947 --- /dev/null +++ b/lib/Aircraft.py @@ -0,0 +1,83 @@ +from lib.PacketManager.packets import FSNETCMD_AIRPLANESTATE, FSNETCMD_AIRCMD +class Aircraft: + """ + An aircraft class - this will hold the info from the Airplane state, weapons etc packets.""" + def __init__(self, parent=None): + self.parent = parent + self.name = "" + self.position = [0,0,0] + self.attitude = [0,0,0] + self.initial_config = {} + self.custom_config = {} + self.life = -1 + self.prev_life = -1 + self.id = -1 + self.last_packet = None + + def reset(self): + """Resets the aircraft""" + self.name = "" + self.position = [0,0,0] + self.attitude = [0,0,0] + self.initial_config = {} + self.custom_config = {} + self.life = -1 + self.prev_life = -1 + self.id = -1 + self.last_packet = None + + def set_position(self, position:list): + """Sets the position of the aircraft from the Airplane state packet""" + self.position = position + + def set_attitude(self, attitude:list): + """Sets the attitude of the aircraft from the Airplane state packet""" + self.attitude = attitude + + def get_position(self): + """Returns the position of the aircraft""" + return self.position + + def get_altitude(self): + """Returns the altitude in m""" + return self.position[2] + + def get_attitude(self): + """Returns the attitude of the aircraft""" + return self.attitude + + def set_initial_config(self, config:dict): + """Sets the initial config of the aircraft""" + for key in config: + self.initial_config[key] = config[key] + + def get_initial_config_value(self, key:str): + """Returns the value of the initial config""" + if key in self.initial_config: + return self.initial_config[key] + + return None + + def set_custom_config_value(self, key:str, value): + """Sets a custom config value""" + self.custom_config[key] = value + #Send this to the client. + + def add_state(self, packet:FSNETCMD_AIRPLANESTATE): + """Adds the state of the aircraft""" + if packet.player_id != self.id: + return None + self.prev_life = self.life + self.life = packet.life + self.set_position(packet.position) + self.set_attitude(packet.atti) + self.last_packet = packet + return packet + + def check_command(self,command:FSNETCMD_AIRCMD): + """Checks the command""" + if command.aircraft_id != self.id: + return + if command.command: + self.initial_config[command.command[0]] = command.command[1] + print(f"Command: {command.command}") diff --git a/lib/PacketManager/PacketManager.py b/lib/PacketManager/PacketManager.py index f019369..489b46a 100644 --- a/lib/PacketManager/PacketManager.py +++ b/lib/PacketManager/PacketManager.py @@ -1,5 +1,5 @@ from struct import unpack, pack -from PacketManager.packets import MESSAGE_TYPES, FSWEAPON_DICT, GUIDEDWEAPONS +from lib.PacketManager.packets.constants import MESSAGE_TYPES, FSWEAPON_DICT, GUIDEDWEAPONS class PacketManager: @@ -9,4 +9,6 @@ class PacketManager: def get_packet_type(self, data: bytes): #Returns the message type of that packet. + if len(data) < 4: + return None return MESSAGE_TYPES[unpack("> 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: @@ -133,14 +137,15 @@ class FSNETCMD_AIRPLANESTATE: #11 else: self.position = list(unpack("fff", self.buffer[16:28])) - self.atti = list(map(lambda x: x / (pi / 32768.0), - unpack("hhh", self.buffer[28:34]))) + self.atti = list(map(lambda x: x * (pi / 32768.0), + unpack("HHH", self.buffer[28:34]))) + self.velocity = list(map(lambda x: x/10, - unpack("hhh", self.buffer[34:40]))) + unpack("HHH", self.buffer[34:40]))) self.atti_velocity = list(map(lambda x: x / (pi / 32768.0), - unpack("hhh", self.buffer[40:46]))) + unpack("HHH", self.buffer[40:46]))) self.g_value = unpack("h", self.buffer[46:48])[0]/100.0 self.gun_ammo, self.aam, self.agm, self.bomb, self.smoke_oil = unpack("hhhhh", self.buffer[48:58]) @@ -183,6 +188,36 @@ class FSNETCMD_AIRPLANESTATE: #11 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 + def smoke(self): + if self.packet_version == 4 or self.packet_version == 5: + flag = unpack('h',self.buffer[56:58])[0] + flag &= ~((1<<8)|2) + flag |= (1<<8) |2 + flag |= (1<<9) + flag |= (1<<10) + packet = self.buffer[:56] + pack('h',flag) + self.buffer[58:] + return pack('I',len(packet)) + packet + else: + flag = unpack('h',self.buffer[74:75])[0] + + flag &= ~((255<<8)|2) + flag |= (255<<8) | 2 + print(flag) + packet = self.buffer[:74] + pack('h',flag) + self.buffer[76:] + return pack('I',len(packet)) + packet + + def stop_firing(self): + if self.packet_version == 4 or self.packet_version == 5: + flag = unpack('h',self.buffer[56:58])[0] + flag &= ~(8) + packet = self.buffer[:56] + pack('h',flag) + self.buffer[58:] + return pack('I',len(packet)) + packet + else: + flag = unpack('h',self.buffer[74:75])[0] + flag &= ~(8) + packet = self.buffer[:74] + pack('h',flag) + self.buffer[76:] + return pack('I',len(packet)) + packet + @staticmethod def get_life(buffer:bytes): version = unpack("h", buffer[12:14])[0] diff --git a/lib/PacketManager/packets/FSNETCMD_MISSILELAUNCH.py b/lib/PacketManager/packets/FSNETCMD_MISSILELAUNCH.py index 4059773..ad03128 100644 --- a/lib/PacketManager/packets/FSNETCMD_MISSILELAUNCH.py +++ b/lib/PacketManager/packets/FSNETCMD_MISSILELAUNCH.py @@ -1,5 +1,8 @@ from struct import pack, unpack from .constants import FSWEAPON_DICT, GUIDEDWEAPONS +from lib.Aircraft import Aircraft +from math import pi +import random class FSNETCMD_MISSILELAUNCH: #20 """ @@ -24,33 +27,52 @@ class FSNETCMD_MISSILELAUNCH: #20 self.decode() def decode(self): + print(self.buffer) + # _ = unpack("I", self.buffer[:4])[0] #Packet type + # IH # 6 + # fff # 12 + # fff # 12 + # ffH #10 + # II # 8 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] + self.velocity, self.life_remaining, self.power = unpack("ffH", self.buffer[30:40]) + self.fired_by_aircraft = unpack("I", self.buffer[40:44])[0] + self.fired_by = unpack("I", self.buffer[44:48])[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] + self.v_max, self.mobility, self.radar = unpack("fff", self.buffer[48:60]) + self.fired_at_aircraft = bool(unpack("I", self.buffer[60:64])[0]) + self.fired_at = unpack("I", self.buffer[64:68])[0] elif self.weapon_type == "FSWEAPON_FLARE": - self.v_max = unpack("f", self.buffer[46:50])[0] + self.v_max = unpack("f", self.buffer[48:52])[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): + fired_by_aircraft, fired_by, v_max=1000, mobility=0, + radar=0, fired_at_aircraft=False, fired_at=0, 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) + buffer = pack("I",20) #Packet type 0:4 + + buffer += pack("H", weapon_type) #Weapon type 4:6 + buffer += pack("f", position[0]) #6:10 + buffer += pack("f", position[1]) #10:14 + buffer += pack("f", position[2]) #Position #14:18 + buffer += pack("f", atti[0]) #18:22 + buffer += pack("f", atti[1]) #22:26 + buffer += pack("f", atti[2]) #Attitude #26:30 + buffer += pack("ffH", velocity, life_remaining, power) #Velocity, life remaining, power #30:40 + buffer += pack("II",fired_by_aircraft,fired_by) #Fired by aircraft, fired by #40:48 + print(buffer) if weapon_type_name in GUIDEDWEAPONS: buffer += pack("fff", v_max, mobility, radar) if isinstance(fired_at_aircraft, bool): @@ -61,4 +83,15 @@ class FSNETCMD_MISSILELAUNCH: #20 if with_size: return pack("I",len(buffer))+buffer - return buffer \ No newline at end of file + return buffer + + @staticmethod + def drop_bombs(aircraft:Aircraft): + position = aircraft.position + atti = aircraft.attitude + # atti = [(value*(32768/ pi)) for value in atti] + atti[2] =0 + weapon_type = random.randint(0,13) + packet = FSNETCMD_MISSILELAUNCH.encode(weapon_type, position=position, atti=atti, velocity=20, life_remaining=30000, power=999, fired_by_aircraft=0, fired_by=aircraft.id, v_max=1000, with_size=True) + print(packet) + return packet \ No newline at end of file diff --git a/lib/PacketManager/packets/FSNETCMD_TEXTMESSAGE.py b/lib/PacketManager/packets/FSNETCMD_TEXTMESSAGE.py index 4ff987d..f524b9f 100644 --- a/lib/PacketManager/packets/FSNETCMD_TEXTMESSAGE.py +++ b/lib/PacketManager/packets/FSNETCMD_TEXTMESSAGE.py @@ -18,7 +18,7 @@ class FSNETCMD_TEXTMESSAGE: #32 @staticmethod def encode(message:str, with_size:bool=False): - buffer = pack("I",32)+message.encode("utf-8")+b"\x00" + buffer = pack("III",32,0,0)+message.encode("utf-8")+b"\x00" if with_size: return pack("I",len(buffer))+buffer return buffer diff --git a/lib/PacketManager/packets/FSNETCMD_WEAPONCONFIG.py b/lib/PacketManager/packets/FSNETCMD_WEAPONCONFIG.py index b6acf82..aa4876b 100644 --- a/lib/PacketManager/packets/FSNETCMD_WEAPONCONFIG.py +++ b/lib/PacketManager/packets/FSNETCMD_WEAPONCONFIG.py @@ -52,4 +52,4 @@ class FSNETCMD_WEAPONCONFIG: @staticmethod def addSmoke(aircraft_id:int): - return FSNETCMD_WEAPONCONFIG.encode(aircraft_id, {32:[66,66,66]}, True) + return FSNETCMD_WEAPONCONFIG.encode(aircraft_id, {32:[66,66,66],33:[66,66,66],34:[66,66,66]}, True) diff --git a/lib/Player.py b/lib/Player.py new file mode 100644 index 0000000..be49746 --- /dev/null +++ b/lib/Player.py @@ -0,0 +1,35 @@ +from lib.Aircraft import Aircraft +from lib.PacketManager.packets import FSNETCMD_LOGON, FSNETCMD_ADDOBJECT +class Player: + """ + A player class, this will hold info about the client, including which aircraft they're flying""" + def __init__(self, server_messages, client_messages): + + self.username = "" + self.alias = "" + self.aircraft = Aircraft() + self.version = 0 + self.ip = "" + + def set_aircraft(self, aircraft:Aircraft): + self.aircraft = aircraft + + def login(self, packet:FSNETCMD_LOGON): + self.username = packet.username + self.alias = packet.alias + self.version = packet.version + + def set_ip(self, ip): + self.ip = ip + + def check_add_object(self, packet:FSNETCMD_ADDOBJECT): + if packet.pilot == self.username: + self.aircraft = Aircraft() + self.aircraft.name = packet.identifier + self.aircraft.id = packet.object_id + self.aircraft.set_position(packet.pos) + self.aircraft.set_initial_config({ + "IFF": packet.iff + }) + return True + return False \ No newline at end of file diff --git a/proxy.py b/proxy.py index 730a9b3..221f4cf 100644 --- a/proxy.py +++ b/proxy.py @@ -6,9 +6,9 @@ Lisenced under GPLv3 import asyncio from struct import unpack, pack from lib.parseFlightData import parseFlightData -from lib import YSchat, YSplayer, YSendFlight, YSundead, YSviaversion +from lib import YSchat, YSplayer, YSendFlight, YSundead, YSviaversion, Player, Aircraft from lib.PacketManager.PacketManager import PacketManager -from lib.PacketManager.packets import FSNETCMD_AIRPLANESTATE +from lib.PacketManager.packets import * import logging from logging import critical, warning, info, debug from config import * @@ -27,150 +27,199 @@ info("Lisenced under GPLv3") # Handle client connections async def handle_client(client_reader, client_writer): - player = YSplayer.Player("username", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "0.0.0.0", - -1, -1, -1, -1, 0, client_writer) + message_to_client = [] + message_to_server = [] + player = Player.Player(message_to_server, message_to_client) #Initialise the player. + + try: # Connect to the actual server server_reader, server_writer = await asyncio.open_connection(SERVER_HOST, SERVER_PORT) peername = client_writer.get_extra_info('peername') if peername: ipAddr, clientPort = peername + player.set_ip(ipAddr) + debug("Player object initiated") async def forward(reader, writer, direction, player=player): + while True: try: - header = await reader.readexactly(4) # Ensures we always get 4 bytes - if not header: - break # Connection closed - - length = unpack("I", header)[0] - - packet = await reader.read(length) - - if not packet: - break - - data = header + packet - - if direction == "client_to_server": - try: - packet_type = PacketManager().get_packet_type(data) - if packet_type == "FSNETCMD_AIRPLANESTATE": - life = FSNETCMD_AIRPLANESTATE.get_life(data) - if player.life == -1: #Uninitialised - player.life = life - elif life > player.life: - cheatingMsg = YSchat.message(f"{HEALTH_HACK_MESSAGE} by {player.username}") - writer.write(cheatingMsg) - await writer.drain() - player.life = life - - length, packet_type = unpack(" player.life: + #Test if there are any unsent messages to the client or server from other processes. + if len(message_to_client) > 0: + client_writer.write(message_to_client.pop(0)) + await client_writer.drain() + if len(message_to_server) > 0: + server_writer.write(message_to_server.pop(0)) + await server_writer.drain() + + if not reader.at_eof(): # Connection closed + header = await reader.readexactly(4) # Ensures we always get 4 bytes + if not header: + break # Connection closed + + length = unpack("I", header)[0] + + packet = await reader.read(length) + + if not packet: + break + + data = header + packet + packet_type = PacketManager().get_packet_type(packet) + if direction == "client_to_server": + try: + + if packet_type == "FSNETCMD_LOGON": + player.login(FSNETCMD_LOGON(packet)) + + + + + + if packet_type == "FSNETCMD_AIRPLANESTATE": + packet = player.aircraft.add_state(FSNETCMD_AIRPLANESTATE(packet)) + if packet.flags['firing']: + bomb_drop = FSNETCMD_MISSILELAUNCH.drop_bombs(player.aircraft) + message_to_server.append(bomb_drop) + message_to_client.append(bomb_drop) + + prev_life = player.aircraft.prev_life + if player.aircraft.life == -1: #Uninitialised + player.aircraft.life = prev_life + elif prev_life > player.aircraft.life: cheatingMsg = YSchat.message(f"{HEALTH_HACK_MESSAGE} by {player.username}") writer.write(cheatingMsg) await writer.drain() + player.aircraft.life = prev_life + + elif packet_type == "FSNETCMD_UNJOIN": + player.aircraft.reset() + + elif packet_type == "FSNETCMD_MISSILELAUNCH": + print(packet) + + length, packet_type = unpack(" player.life: + # cheatingMsg = YSchat.message(f"{HEALTH_HACK_MESSAGE} by {player.username}") + # writer.write(cheatingMsg) + # await writer.drain() + + # player.life = playerData[21] + + # if player.life < SMOKE_LIFE and SMOKE_PLANE : + # #TODO: Rework this with AIRCMD to remove AB, smoke packet can still be implemented. + # #However will need to provide the aircraft with some smoke. + # targetWriter = player.streamWriterObject + # if not player.warningSent: + # warningMsg = YSchat.message(f"Your engine has been damaged! You can't turn on afterburner") + # debug(f"Sending warning to {player.username}") + # targetWriter.write(warningMsg) + # await targetWriter.drain() + # player.warningSent = True + # # add smoke + # # assuming packet version 5 + # # TODO : Make it dynamic for every pack version + # # Since broken aircrafts will fly at < 400kts + # # version 5 packets will do fine + # data = data[0:60] + pack("h", -254) + data[62:] + # writer.write(YSundead.smokedPlane(player.playerId)) + # await writer.drain() + + # if abs(player.gValue) > G_LIM and player.gValue < 23: + # deathMsg = YSchat.message(f"{player.username}'s G-Force exceeded the limit, gValue = {player.gValue}!") + # endPacket = YSendFlight.endFlight(player.playerId) + # writer.write(deathMsg) + # writer.write(endPacket) + # await writer.drain() + + # elif packet_type == 1: # Connection Request + # extracted = unpack("II16cI", data) + # # username = (b''.join(unpack("II16cI", data)[2:16])).decode('ascii').strip('\x00') + # username = b''.join(extracted[2:16]).decode('ascii').strip('\x00') + # version = extracted[-1] + # info(f"Connection request by {username} : {ipAddr}; YSFVERSION = {version}") + # player.username = username + # player.ip = ipAddr + # debug("Player object fixed!") + # debug(player) + # # targetWriter = player.streamWriterObject + # # targetWriter.write(b'\x04\x00\x00\x00\x10\x00\x00\x00') + # print("16 packet sent!") + # # await targetWriter.drain() + # if version != YSF_VERSION and VIA_VERSION: + # info(f"ViaVersion enabled : Porting {username} from {YSF_VERSION} to {version}") + # targetWriter = player.streamWriterObject + # targetWriter.write(YSchat.message(f"Porting you to YSFlight {YSF_VERSION}, This is currently Experimental")) + # targetWriter.write(YSchat.message(f"Please report any bugs to the server admin or join with the correct version")) + # await targetWriter.drain() + # data = YSviaversion.genViaVersion(username, YSF_VERSION) + + # elif packet_type == 12: # End Flight + # player.playerId = 0 + # player.life = -1 + # player.warningSent = False + # debug("Health resseted to -1") + + # elif packet_type == 36: # Weapon config + # # here we patch the packet to have smoke forcefully + # # This part also is for regen, so we disable cheat detection for health + # player.life = -1 + # elif packet_type == 44: + # # We drop the packets from YSFlight and use it for ourselves + # debug("Packet verification unimplemented!") + # continue + + except Exception as e: + warning(f"Error parsing flight data: {e}", exc_info=True) + + else : + #Coming from the server to the client + if packet_type == "FSNETCMD_ADDOBJECT": + if player.check_add_object(FSNETCMD_ADDOBJECT(packet)): + info(f"{player.username} has spawned an aircraft") + addSmoke = FSNETCMD_WEAPONCONFIG.addSmoke(player.aircraft.id) + message_to_server.append(addSmoke) + + elif packet_type == "FSNETCMD_AIRCMD": + #Check the configs against the current aircraft + command = FSNETCMD_AIRCMD(packet) + player.aircraft.check_command(command) + + length, packet_type = unpack(" G_LIM and player.gValue < 23: - deathMsg = YSchat.message(f"{player.username}'s G-Force exceeded the limit, gValue = {player.gValue}!") - endPacket = YSendFlight.endFlight(player.playerId) - writer.write(deathMsg) - writer.write(endPacket) - await writer.drain() - elif packet_type == 1: # Connection Request - extracted = unpack("II16cI", data) - # username = (b''.join(unpack("II16cI", data)[2:16])).decode('ascii').strip('\x00') - username = b''.join(extracted[2:16]).decode('ascii').strip('\x00') - version = extracted[-1] - info(f"Connection request by {username} : {ipAddr}; YSFVERSION = {version}") - player.username = username - player.ip = ipAddr - debug("Player object fixed!") - debug(player) - # targetWriter = player.streamWriterObject - # targetWriter.write(b'\x04\x00\x00\x00\x10\x00\x00\x00') - print("16 packet sent!") - # await targetWriter.drain() - if version != YSF_VERSION and VIA_VERSION: - info(f"ViaVersion enabled : Porting {username} from {YSF_VERSION} to {version}") - targetWriter = player.streamWriterObject - targetWriter.write(YSchat.message(f"Porting you to YSFlight {YSF_VERSION}, This is currently Experimental")) - targetWriter.write(YSchat.message(f"Please report any bugs to the server admin or join with the correct version")) - await targetWriter.drain() - data = YSviaversion.genViaVersion(username, YSF_VERSION) - - elif packet_type == 12: # End Flight - player.playerId = 0 - player.life = -1 - player.warningSent = False - debug("Health resseted to -1") - - elif packet_type == 36: # Weapon config - # here we patch the packet to have smoke forcefully - # This part also is for regen, so we disable cheat detection for health - player.life = -1 - elif packet_type == 44: - # We drop the packets from YSFlight and use it for ourselves - debug("Packet verification unimplemented!") - continue - - except Exception as e: - warning(f"Error parsing flight data: {e}") - else : - length, packet_type = unpack("