aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/PacketManager.py708
-rw-r--r--lib/YSplayer.py30
2 files changed, 679 insertions, 59 deletions
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
+
+