From 4148580ff8d56ef30d22a3fcea521237fc8ee627 Mon Sep 17 00:00:00 2001 From: Nicholas Hammond Date: Sat, 17 Feb 2018 17:56:02 -0600 Subject: [PATCH 1/9] Updated some things for 2.7 Fixed some packet unpackings Updated some enums Updated object_update --- diana/encoding.py | 39 ++++++++++++++++++++++++-- diana/enumerations.py | 31 +++++++++++---------- diana/object_update.py | 62 +++++++++++++++++++++++++++++++++++------- diana/packet.py | 8 +++--- 4 files changed, 109 insertions(+), 31 deletions(-) diff --git a/diana/encoding.py b/diana/encoding.py index d3290e8..a9ae2c2 100644 --- a/diana/encoding.py +++ b/diana/encoding.py @@ -76,20 +76,46 @@ def st_decode(fmt, data, handle_trail): def decode_unicode_string(fmt, data, handle_trail): if len(data) < 4: raise ValueError('Truncated data') + #print("\t",":".join("{:02x}".format(c) for c in data[:4])) str_len_padded, = struct.unpack(''.format(self.ships) From 54381689eb6cfc11380bf21e8e38c5031b11d293 Mon Sep 17 00:00:00 2001 From: Nicholas Hammond Date: Sat, 17 Feb 2018 17:58:30 -0600 Subject: [PATCH 2/9] added example example is very simple --- example.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 example.py diff --git a/example.py b/example.py new file mode 100644 index 0000000..a3cbc9d --- /dev/null +++ b/example.py @@ -0,0 +1,19 @@ +import diana +import diana.tracking +import pprint +import random + +pp = pprint.PrettyPrinter(indent=4) + +tx, rx = diana.connect('127.0.0.1') # or whatever IP +tx(diana.packet.SetShipPacket(0)) # Select Ship 0 (Artemis) +tx(diana.packet.SetConsolePacket(diana.packet.Console.data, True)) +tx(diana.packet.ReadyPacket()) +tracker = diana.tracking.Tracker() +while True: + for packet in rx: # stream packets from the server + tracker.rx(packet) # Update the tracker with new information + #print(packet) + #pp.pprint(tracker.objects) + if (random.random() > 0.99): + pp.pprint(tracker.player_ship) From 486bd564000c9be4dae0d8193ac31b39c3c89710 Mon Sep 17 00:00:00 2001 From: Nicholas Hammond Date: Wed, 21 Feb 2018 22:21:07 -0600 Subject: [PATCH 3/9] Completely changed the object update stuff --- diana/object_update.py | 437 +++++++++++++++++++++++++++++++++++++---- example.py | 27 ++- 2 files changed, 415 insertions(+), 49 deletions(-) mode change 100644 => 100755 example.py diff --git a/diana/object_update.py b/diana/object_update.py index bf02b65..abf65bf 100644 --- a/diana/object_update.py +++ b/diana/object_update.py @@ -1,3 +1,4 @@ +import math from .encoding import decode as unpack from .enumerations import * @@ -5,6 +6,319 @@ def unscramble_elites(field): return {ability for ability in EliteAbility if ability.value & field} +Object_Properties = { + ObjectType.anomaly : { + "1.1" : ('x','f'), + "1.2" : ('y','f'), + "1.3" : ('z','f'), + "1.4" : ('upgrade','int') + }, + ObjectType.base : { + "1.1" : ('name','u'), + "1.2" : ('shields','f'), + "1.3" : ('aft-shields','f'), + "1.4" : ('index','I'), + "1.5" : ('vessel-type','I'), + "1.6" : ('x','f'), + "1.7" : ('y','f'), + "1.8" : ('z','f'), + + "2.1" : ('unknown-1','I'), + "2.2" : ('unknown-2','I'), + "2.3" : ('unknown-3','I'), + "2.4" : ('unknown-4','I'), + "2.5" : ('unknown-5','B'), + "2.6" : ('unknown-6','B') + }, + ObjectType.creature : { + "1.1" : ('x','f'), + "1.2" : ('y','f'), + "1.3" : ('z','f'), + "1.4" : ('name','u'), + "1.5" : ('heading','f'), + "1.6" : ('pitch','f'), + "1.7" : ('roll','f'), + "1.8" : ('creature-type','I'), + + "2.1" : ('unknown-1','I'), + "2.2" : ('unknown-2','I'), + "2.3" : ('unknown-3','I'), + "2.4" : ('unknown-4','I'), + "2.5" : ('unknown-5','I'), + "2.6" : ('unknown-6','I') + }, + ObjectType.drone : { + "1.1" : ('unknown-1','I'), + "1.2" : ('x','f'), + "1.3" : ('pitch/yaw?','f'), # seen from -0.01 to 53.7 + "1.4" : ('z','f'), + "1.5" : ('pitch/yaw?-2','f'), + "1.6" : ('y','f'), + "1.7" : ('heading','f'), + "1.8" : ('unknown-2','I'), + + "2.1" : ('unknown-3','I'), + "2.2" : ('unknown-4','f') + }, + ObjectType.engineering_console : { + "1.1" : ('beams-heat','f'), # heat levels (f*) + "1.2" : ('torps-heat','f'), + "1.3" : ('sensors-heat','f'), + "1.4" : ('maneuvering-heat','f'), + "1.5" : ('impulse-heat','f'), + "1.6" : ('warp-heat','f'), + "1.7" : ('shields-heat','f'), + "1.8" : ('aft-shields-heat','f'), + + "2.1" : ('beams-energy','f'), # energy levels (f*) + "2.2" : ('torps-energy','f'), + "2.3" : ('sensors-energy','f'), + "2.4" : ('maneuvering-energy','f'), + "2.5" : ('impulse-energy','f'), + "2.6" : ('warp-energy','f'), + "2.7" : ('shields-energy','f'), + "2.8" : ('aft-shields-energy','f'), + + "3.1" : ('beams-coolant','B'), # coolant levels (b*) + "3.2" : ('torps-coolant','B'), + "3.3" : ('sensors-coolant','B'), + "3.4" : ('maneuvering-coolant','B'), + "3.5" : ('impulse-coolant','B'), + "3.6" : ('warp-coolant','B'), + "3.7" : ('shields-coolant','B'), + "3.8" : ('aft-shields-coolant','B') + }, + ObjectType.mesh : { # generic mesh + "1.1" : ('x','f'), + "1.2" : ('y','f'), + "1.3" : ('z','f'), + "1.4" : ('unk-1','I'), + "1.5" : ('unk-2','I'), + "1.6" : ('unk-3','II'), # long + "1.7" : ('unk-4','I'), + "1.8" : ('unk-5','I'), + + "2.1" : ('unk-6','I'), + "2.2" : ('unk-7','II'), # long + "2.3" : ('name','u'), + "2.4" : ('meshtexture','I'), # meshfile and/or texturefile... i guess. + #2.5 unused + "2.6" : ('unk-8','I'), + "2.7" : ('unk-9','S'), + "2.8" : ('unk-a','B'), + + "3.1" : ('red','f'), + "3.2" : ('green','f'), + "3.3" : ('blue','f'), + "3.4" : ('fore-shields','f'), + "3.5" : ('aft-shields','f'), + #... more unknown + + "4.1" : ('unk-b','I'), + "4.2" : ('unk-c','I') + }, + ObjectType.nebula : { + "1.1" : ('x','f'), + "1.2" : ('y','f'), + "1.3" : ('z','f'), + "1.4" : ('red','f'), + "1.5" : ('green','f'), + "1.6" : ('blau','f'), + "1.7" : ('unused?-1','I'), + "1.8" : ('unused?-2','I') + }, + ObjectType.other_ship : { # NPC ship + "1.1" : ('name', 'u'), # im too indecisive to pick whether + "1.2" : ('throttle', 'f'), # i want any tab-alignment on this + "1.3" : ('rudder', 'f'), # at all, or where when i do + "1.4" : ('max-impulse', 'f'), + "1.5" : ('max-turn-rate', 'f'), # NONE OF IT LOOKS RIGHT + "1.6" : ('enemy?', 'I'), + "1.7" : ('vessel-type', 'I'), + "1.8" : ('x', 'f'), + + "2.1" : ('y', 'f'), # i dont know why im even working on this library + "2.2" : ('z', 'f'), # in the first place + "2.3" : ('pitch', 'f'), + "2.4" : ('roll', 'f'), + "2.5" : ('yaw', 'f'), # i should be studying + "2.6" : ('heading', 'f'), + "2.7" : ('velocity','f'), + "2.8" : ('surrendered','B'), # why are so many of these fields being sent + + "3.1" : ('forward-shields','f'), + "3.2" : ('forward-shields-max','f'), + "3.3" : ('aft-shields','f'), + "3.4" : ('aft-shields-max','f'), + "3.5" : ('unknown-1','S'), + "3.6" : ('fleet-number','B'), + "3.7" : ('special-abilities','I'), + "3.8" : ('special-abilities-active','I'), + + "4.1" : ('scan-level?','I'), + "4.2" : ('side?','I'), + "4.3" : ('unknown-2','I'), + "4.4" : ('unknown-3','B'), + "4.5" : ('unknown-4','B'), + "4.6" : ('unknown-5','B'), + "4.7" : ('unknown-6','B'), + "4.8" : ('unknown-7','f'), + + "5.1" : ('unknown-8','I'), + "5.2" : ('unknown-9','I'), + "5.3" : ('sys-beams-damage','f'), # system damage + "5.4" : ('sys-torps-damage','f'), + "5.5" : ('sys-sensors-damage','f'), + "5.6" : ('sys-maneuvering-damage','f'), + "5.7" : ('sys-impulse-damage','f'), + "5.8" : ('sys-warp-damage','f'), + + "6.1" : ('sys-fore-shields-damage','f'), + "6.2" : ('sys-aft-shields-damage','f'), + "6.3" : ('freq-a-resistance','f'), # beam freq resistances + "6.4" : ('freq-b-resistance','f'), + "6.5" : ('freq-c-resistance','f'), + "6.6" : ('freq-d-resistance','f'), + "6.7" : ('freq-e-resistance','f') + }, + ObjectType.player_vessel : { + "1.1" : ('weapons-target','I'), + "1.2" : ('impulse','f'), + "1.3" : ('rudder','f'), + "1.4" : ('top-speed','f'), + "1.5" : ('turn-rate','f'), + "1.6" : ('auto-beams','B'), + "1.7" : ('warp-factor','B'), + "1.8" : ('energy-reserves','f'), + + "2.1" : ('shields-up/down','S'), + "2.2" : ('unknown-1','I'), + "2.3" : ('ship-type?','I'), + "2.4" : ('x','f'), + "2.5" : ('y','f'), + "2.6" : ('z','f'), + "2.7" : ('pitch','f'), + "2.8" : ('roll','f'), + + "3.1" : ('heading','f'), + "3.2" : ('velocity','f'), + "3.3" : ('in-a-nebula','S'), + "3.4" : ('ship-name','u'), + "3.5" : ('forward-shields','f'), + "3.6" : ('forward-shields-max','f'), + "3.7" : ('aft-shields','f'), + "3.8" : ('aft-shields-max','f'), + + "4.1" : ('last-docked-base','I'), + "4.2" : ('alert-status','B'), + "4.3" : ('unknown-2','f'), + "4.4" : ('main-screen-view','B'), + "4.5" : ('beam-frequency','B'), + "4.6" : ('available-coolant-or-missiles','B'), + "4.7" : ('science-target','I'), + "4.8" : ('captain-target','I'), + + "5.1" : ('drive-type','B'), + "5.2" : ('scanning-id','I'), + "5.3" : ('scanning-progress','f'), + "5.4" : ('reverse','B'), + "5.5" : ('unknown-3','f'), # 0.0 - 1.0 observed + "5.6" : ('unknown-4','B'), # 0x02 observed + "5.7" : ('visibility?','I'), # 0, 1 observed + "5.8" : ('ship-index?','B'), + + "6.1" : ('capital-ship-object-id','I'), + "6.2" : ('accent-color','f'), # colour >:l + "6.3" : ('unknown-5','I'), + "6.4" : ('beacon-creature-type','I'), + "6.5" : ('beacon-mode','B') + }, + #ObjectType.player_ship_upgrade : { + # }, + ObjectType.torpedo : { + "1.1" : ('x','f'), + "1.2" : ('y','f'), + "1.3" : ('z','f'), + "1.4" : ('delta-x','f'), + "1.5" : ('delta-y','f'), + "1.6" : ('delta-z','f'), + "1.7" : ('unknown-1','I'), + "1.8" : ('ordnance-type','I'), + + "2.1" : ('unknown-2','B'), # there's two bytes of bitfields... + "2.2" : ('a','B'), # im lazy, these are unknown/unused fields + "2.3" : ('aa','B'), + "2.4" : ('aaa','B'), + "2.5" : ('aaaa','B'), + "2.6" : ('bbbb','B'), + "2.7" : ('bbb','B'), + "2.8" : ('bb','B') + }, + ObjectType.weapons_console : { + "1.1" : ('homing-count','B'), + "1.2" : ('nuke-count','B'), + "1.3" : ('mine-count','B'), + "1.4" : ('emp-count','B'), + "1.5" : ('pshock-count','B'), + "1.6" : ('beacon-count','B'), + "1.7" : ('probe-count','B'), + "1.8" : ('tag-count','B'), + + "2.1" : ('tube-1-time','f'), + "2.2" : ('tube-2-time','f'), + "2.3" : ('tube-3-time','f'), + "2.4" : ('tube-4-time','f'), + "2.5" : ('tube-5-time','f'), + "2.6" : ('tube-6-time','f'), + "2.7" : ('tube-1-status','B'), + "2.8" : ('tube-2-status','B'), + + "3.1" : ('tube-3-status','B'), + "3.2" : ('tube-4-status','B'), + "3.3" : ('tube-5-status','B'), + "3.4" : ('tube-6-status','B'), + "3.5" : ('tube-1-type','B'), + "3.6" : ('tube-2-type','B'), + "3.7" : ('tube-3-type','B'), + "3.8" : ('tube-4-type','B'), + + "4.1" : ('tube-5-type','B'), + "4.2" : ('tube-6-type','B') + }, + ObjectType.asteroid : { + "1.1" : ('x','f'), + "1.2" : ('y','f'), + "1.3" : ('z','f'), + "1.4" : ('name','u'), + } + } +def itobf(x): + prefix = math.floor(x/8) + 1 + suffix = (x%8) + 1 + return str(prefix)+'.'+str(suffix) + +player_ship_upgrades_list = [ 'infusion_p_coils', 'hydrogen_ram', 'tauron_focusers', + 'carapaction_coils', 'polyphasic_capacitors', 'cetrocite_crystals', 'lateral_array', + 'ecm_starpulse', 'double_agent', 'wartime_production', 'infusion_p_coils_perm', + 'protonic_verniers', 'tauron_focusers_perm', 'regenerative_pau_grids', + 'veteran_damcon_teams', 'cetrocite_heatsinks', 'tachyon_scanners', + 'gridscan_overload', 'override_authorization', 'resupply_imperatives', + 'patrol_group', 'fast_supply', 'vanguard_refit_helm', 'vanguard_refit_weap', + 'vanguard_refit_comm', 'vanguard_refit_station', 'vanguard_refit_eng', + 'vanguard_refit_systems' ] # so many oh my g*sh + +ply_shp_upgrd = {} +i=0 +for field,ftype in (('active_','B'),('count_','B'),('time_','S')): + for upgrade in player_ship_upgrades_list: + ply_shp_upgrd[itobf(i)] = (field+upgrade,ftype) + i+=1 +# no way in hell i was writing that out one-by-one. 84 bitfields! +Object_Properties[ObjectType.player_ship_upgrade] = ply_shp_upgrd + +Object_Properties[ObjectType.mine] = Object_Properties[ObjectType.asteroid] +Object_Properties[ObjectType.blackhole] = Object_Properties[ObjectType.asteroid] + def decode_obj_update_packet(packet): entries = [] while packet: @@ -12,7 +326,40 @@ def decode_obj_update_packet(packet): obj = {} if update_type == 0x00: break - elif update_type == 0x01: + if ObjectType(update_type) in Object_Properties.keys(): + properties = Object_Properties[ObjectType(update_type)] + fieldcount = max([int(fk.split('.')[0]) for fk in properties.keys()]) + unpacked = list(unpack('BIfbs*'.replace('fbs','B'*fieldcount),packet)) + # _id = unpacked[0] + # oid = unpacked[1] + # fields_k = unpacked[1+k] + # packet = unpacked[2+fieldcount] + obj['object'] = unpacked[1] # oid + obj['type'] = ObjectType(update_type) + for fdotb in properties: + prop = properties[fdotb] + f,b = [int(k) for k in fdotb.split(".")] # "byte.bit" -> (byte, bit) + propname, proptype = prop + if unpacked[1+f] & 2**(b-1): # field_f & (bit b) + value,pktremaining = unpack( + '%s*'%proptype, unpacked[2+fieldcount] + ) + obj[propname] = value + unpacked[2+fieldcount] = pktremaining + packet = unpacked[2+fieldcount] + else: + raise ValueError('Unknown object type {}'.format(update_type)) + entries.append(obj) + return entries + + # From here-on is the old object_update code + + while packet: + update_type = packet[0] + obj = {} + if update_type == 0x00: # end of ObjectUpdatePacket + break + elif update_type == ObjectType.player_vessel.value: # player ship _id, oid, fields_1, fields_2, fields_3, fields_4, fields_5, fields_6, packet = unpack('BIBBBBBB*', packet) obj['object'] = oid obj['type'] = ObjectType.player_vessel @@ -123,7 +470,7 @@ def decode_obj_update_packet(packet): raise ValueError('Unknown data keys for player vessel') if fields_6 & 0x80: raise ValueError('Unknown data keys for player vessel') - elif update_type == 0x02: + elif update_type == ObjectType.weapons_console.value: # weapons console _id, oid, fields_1, fields_2, fields_3, packet = unpack('BIBBB*', packet) obj['object'] = oid obj['type'] = ObjectType.weapons_console @@ -187,7 +534,7 @@ def decode_obj_update_packet(packet): obj['contents-5'] = OrdnanceType(ot) if fields_3 & 0x80: raise ValueError('Unknown fields for weapons console') - elif update_type == 0x03: + elif update_type == ObjectType.engineering_console.value: # engineering console _id, oid, fields_heat, fields_enrg, fields_coolant, fields_unk, packet = unpack('BIBBBB*', packet) obj['object'] = oid obj['type'] = ObjectType.engineering_console @@ -208,10 +555,10 @@ def decode_obj_update_packet(packet): for syst, flag in systems: if fields_heat & flag: obj['{}-{}'.format(status, syst)], packet = unpack(fmt + '*', packet) - elif update_type == 0x04: + elif update_type == ObjectType.player_ship_upgrade.value: # player ship upgrades _id, oid, fields, packet = unpack('BIB*', packet) obj['object'] = oid - obj['type'] = ObjectType.anomaly + obj['type'] = ObjectType.player_ship_upgrade if fields & 0x01: obj['x'], packet = unpack('f*', packet) if fields & 0x02: @@ -228,7 +575,7 @@ def decode_obj_update_packet(packet): packet = packet[4:] if fields & 0x80: packet = packet[4:] - elif update_type == 0x05: + elif update_type == ObjectType.other_ship.value: # NPC ship _id, oid, fields_1, fields_2, fields_3, fields_4, fields_5, fields_6, packet = unpack('BIBBBBBB*', packet) obj['object'] = oid obj['type'] = ObjectType.other_ship @@ -333,7 +680,7 @@ def decode_obj_update_packet(packet): obj['shields-4'], packet = unpack('f*', packet) if fields_6 & 0x80: raise ValueError('Unknown data key for NPC') - elif update_type == 0x06: + elif update_type == ObjectType.base.value: # base _id, oid, fields_1, fields_2, packet = unpack('BIBB*', packet) obj['object'] = oid obj['type'] = ObjectType.base @@ -367,7 +714,7 @@ def decode_obj_update_packet(packet): packet = packet[1:] if fields_2 & 0xc0: raise ValueError('Unknown data keys for base') - elif update_type == 0x07: + elif update_type == ObjectType.mine.value: # mine _id, oid, fields, packet = unpack('BIB*', packet) obj['object'] = oid obj['type'] = ObjectType.mine @@ -387,7 +734,27 @@ def decode_obj_update_packet(packet): packet = packet[4:] if fields & 0x80: packet = packet[4:] - elif update_type == 0x08: + elif update_type == ObjectType.anomaly.value: # anomaly + _id, oid, fields, packet = unpack('BIB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.anomaly + if fields & 0x01: + obj['x'], packet = unpack('f*', packet) + if fields & 0x02: + obj['y'], packet = unpack('f*', packet) + if fields & 0x04: + obj['z'], packet = unpack('f*', packet) + if fields & 0x08: + obj['upgrade'], packet = unpack('I*', packet) + if fields & 0x10: + packet = packet[4:] + if fields & 0x20: + packet = packet[4:] + if fields & 0x40: + packet = packet[4:] + if fields & 0x80: + packet = packet[4:] + elif update_type == 0x09: # unused _id, oid, fields, packet = unpack('BIB*', packet) obj['object'] = oid obj['type'] = ObjectType.anomaly @@ -407,7 +774,7 @@ def decode_obj_update_packet(packet): packet = packet[4:] if fields & 0x80: packet = packet[4:] - elif update_type == 0x09: + elif update_type == ObjectType.nebula.value: # nebula _id, oid, fields, packet = unpack('BIB*', packet) obj['object'] = oid obj['type'] = ObjectType.nebula @@ -427,7 +794,7 @@ def decode_obj_update_packet(packet): packet = packet[4:] if fields & 0x80: packet = packet[4:] - elif update_type == 0x0a: + elif update_type == ObjectType.torpedo.value: # torpedo _id, oid, fields, packet = unpack('BIB*', packet) obj['object'] = oid obj['type'] = ObjectType.torpedo @@ -447,7 +814,7 @@ def decode_obj_update_packet(packet): packet = packet[4:] if fields & 0x80: packet = packet[4:] - elif update_type == 0x0b: + elif update_type == ObjectType.blackhole.value: # black hole _id, oid, fields, packet = unpack('BIB*', packet) obj['object'] = oid obj['type'] = ObjectType.blackhole @@ -467,7 +834,7 @@ def decode_obj_update_packet(packet): packet = packet[4:] if fields & 0x80: packet = packet[4:] - elif update_type == 0x0c: + elif update_type == ObjectType.asteroid.value: # asteroid _id, oid, fields, packet = unpack('BIB*', packet) obj['object'] = oid obj['type'] = ObjectType.asteroid @@ -487,10 +854,10 @@ def decode_obj_update_packet(packet): packet = packet[4:] if fields & 0x80: packet = packet[4:] - elif update_type == 0x0e: + elif update_type == ObjectType.mesh.value: # generic mesh _id, oid, fields, packet = unpack('BIB*', packet) obj['object'] = oid - obj['type'] = ObjectType.monster + obj['type'] = ObjectType.mesh if fields & 0x01: obj['x'], packet = unpack('f*', packet) if fields & 0x02: @@ -498,7 +865,7 @@ def decode_obj_update_packet(packet): if fields & 0x04: obj['z'], packet = unpack('f*', packet) if fields & 0x08: - obj['name'], packet = unpack('u*', packet) + packet = packet[4:] if fields & 0x10: packet = packet[4:] if fields & 0x20: @@ -507,39 +874,27 @@ def decode_obj_update_packet(packet): packet = packet[4:] if fields & 0x80: packet = packet[4:] - elif update_type == 0x0f: - _id, oid, fields_1, fields_2, packet = unpack('BIBB*', packet) + elif update_type == ObjectType.creature.value: # creature + _id, oid, fields, packet = unpack('BIB*', packet) obj['object'] = oid - obj['type'] = ObjectType.whale - if fields_1 & 0x01: - obj['name'], packet = unpack('u*', packet) - if fields_1 & 0x02: - packet = packet[4:] - if fields_1 & 0x04: - packet = packet[4:] - if fields_1 & 0x08: + obj['type'] = ObjectType.monster + if fields & 0x01: obj['x'], packet = unpack('f*', packet) - if fields_1 & 0x10: + if fields & 0x02: obj['y'], packet = unpack('f*', packet) - if fields_1 & 0x20: + if fields & 0x04: obj['z'], packet = unpack('f*', packet) - if fields_1 & 0x40: - obj['pitch'], packet = unpack('f*', packet) - if fields_1 & 0x80: - obj['roll'], packet = unpack('f*', packet) - if fields_2 & 0x01: - obj['heading'], packet = unpack('f*', packet) - if fields_2 & 0x02: + if fields & 0x08: + obj['name'], packet = unpack('u*', packet) + if fields & 0x10: packet = packet[4:] - if fields_2 & 0x04: + if fields & 0x20: packet = packet[4:] - if fields_2 & 0x08: + if fields & 0x40: packet = packet[4:] - if fields_2 & 0x10: + if fields & 0x80: packet = packet[4:] - if fields_2 & 0xe0: - raise ValueError('Unknown data keys for whale') - elif update_type == 0x10: + elif update_type == ObjectType.drone.value: # drone _id, oid, fields_1, fields_2, packet = unpack('BIBB*', packet) obj['object'] = oid obj['type'] = ObjectType.drone diff --git a/example.py b/example.py old mode 100644 new mode 100755 index a3cbc9d..f35b26f --- a/example.py +++ b/example.py @@ -1,19 +1,30 @@ +#!/usr/bin/python3 import diana import diana.tracking + import pprint -import random +pp = pprint.PrettyPrinter(indent=2) + +tx, rx = diana.connect('172.16.104.171') # or whatever IP, this is my local server +tx(diana.packet.SetShipPacket(1)) # Select Ship 1 -pp = pprint.PrettyPrinter(indent=4) +for console in diana.packet.Console: # select a bunch of consoles + if console.value not in (1,2,3,4,5): continue + tx(diana.packet.SetConsolePacket(console,True)) + # 1-5: helm,weapons,engineering,science,comms -tx, rx = diana.connect('127.0.0.1') # or whatever IP -tx(diana.packet.SetShipPacket(0)) # Select Ship 0 (Artemis) -tx(diana.packet.SetConsolePacket(diana.packet.Console.data, True)) tx(diana.packet.ReadyPacket()) tracker = diana.tracking.Tracker() + +class ScienceAI: + def update_scans(): + pass + def reset(): + pass + + while True: for packet in rx: # stream packets from the server tracker.rx(packet) # Update the tracker with new information - #print(packet) #pp.pprint(tracker.objects) - if (random.random() > 0.99): - pp.pprint(tracker.player_ship) + pp.pprint(tracker.player_ship) From 18ba53428800586410cdc8e0770d4bd1afd7b8d2 Mon Sep 17 00:00:00 2001 From: Nicholas Hammond Date: Thu, 22 Feb 2018 01:52:56 -0600 Subject: [PATCH 4/9] updated things --- diana/encoding.py | 8 ++++---- diana/enumerations.py | 25 +++++++++++++++++++++++++ diana/packet.py | 8 ++++---- diana/socket.py | 6 +++++- example.py | 7 ++++--- 5 files changed, 42 insertions(+), 12 deletions(-) diff --git a/diana/encoding.py b/diana/encoding.py index a9ae2c2..37aa8d5 100644 --- a/diana/encoding.py +++ b/diana/encoding.py @@ -159,10 +159,10 @@ def chunks(l, n): # i use this for debug xd yield l[i:i + n] def decode(fmt, data, handle_trail=handle_trail_error): #print(DECODERS.keys()) - #print("fmt:\n\t"," ".join("{:02x}".format(ord(c)) for c in fmt)," || ",fmt) - #print("data:") - #for dc in chunks(data,8): - # print(" ".join("{:02x}".format(c) for c in dc)) + print("fmt:\n\t"," ".join("{:02x}".format(ord(c)) for c in fmt)," || ",fmt) + print("data:") + for dc in chunks(data,8): + print(" ".join("{:02x}".format(c) for c in dc)) if fmt == '': return handle_trail(data) return DECODERS[fmt[0]](fmt, data, handle_trail) diff --git a/diana/enumerations.py b/diana/enumerations.py index 8652204..c4f1bf5 100644 --- a/diana/enumerations.py +++ b/diana/enumerations.py @@ -71,6 +71,31 @@ class ShipType(Enum): battleship = 2 missile_cruiser = 3 dreadnought = 4 + carrier = 5 + mine_layer = 6 + juggernaut = 7 + ximni_light_cruiser = 8 + ximni_scout = 9 + ximni_missile_cruiser = 10 + ximni_battleship = 11 + ximni_carrier = 12 + ximni_dreadnought = 13 + strongbow = 14 + longbow = 15 + brigantine = 16 + + tsn_medium_fighter = 100 + tsn_bomber = 101 + tsn_shuttle = 102 + tsn_lr_shuttle = 103 + + zim_fighter = 120 + zim_bomber = 121 + zim_shuttle = 122 + zim_lr_shuttle = 123 + + avenger = 130 + adventure = 131 ShipSettingsRecord = namedtuple('ShipSettingsRecord', 'drive type name') diff --git a/diana/packet.py b/diana/packet.py index fb1606d..d391edd 100644 --- a/diana/packet.py +++ b/diana/packet.py @@ -821,17 +821,17 @@ def __init__(self, object, port, origin, target, x, y, z, auto): self.auto = auto def encode(self): - return pack('IIIIIIIIfffI', - self.object, 0, 1200, + return pack('IIIIIIIIIfffI', + self.object, 1, 1200, self.port, - 1, 1, + 1, 1, 0, self.origin, self.target, self.x, self.y, self.z, 0 if self.auto else 1) @classmethod def decode(cls, packet): - object, _unk1, _unk2, port, _unk3, _unk4, origin, target, x, y, z, auto = unpack('IIIIIIIIfffI', packet) + object, _unk1, _unk2, port, origintype, targettype, _unk3, origin, target, x, y, z, auto = unpack('IIIIIIIIIfffI', packet) return cls(object, port, origin, target, x, y, z, [True, False][auto]) def __str__(self): diff --git a/diana/socket.py b/diana/socket.py index 550bf3f..f7a4102 100644 --- a/diana/socket.py +++ b/diana/socket.py @@ -12,7 +12,11 @@ def rx(): while True: data = sock.recv(BLOCKSIZE) buf += data - packets, buf = packet.decode(data) + try: + packets, buf = packet.decode(data) + except ValueError: + print("Value Error! Packet problems!") + continue for received_packet in packets: yield received_packet return tx, rx() diff --git a/example.py b/example.py index f35b26f..5a128e7 100755 --- a/example.py +++ b/example.py @@ -5,11 +5,12 @@ import pprint pp = pprint.PrettyPrinter(indent=2) -tx, rx = diana.connect('172.16.104.171') # or whatever IP, this is my local server +# 172.16.104.171 +tx, rx = diana.connect('172.89.225.88') # or whatever IP, this is my local server tx(diana.packet.SetShipPacket(1)) # Select Ship 1 for console in diana.packet.Console: # select a bunch of consoles - if console.value not in (1,2,3,4,5): continue + if console.value not in (3,4,5): continue tx(diana.packet.SetConsolePacket(console,True)) # 1-5: helm,weapons,engineering,science,comms @@ -27,4 +28,4 @@ def reset(): for packet in rx: # stream packets from the server tracker.rx(packet) # Update the tracker with new information #pp.pprint(tracker.objects) - pp.pprint(tracker.player_ship) + #pp.pprint(tracker.player_ship) From 74833b67043cdb0640d8672c163a76bcbcfe53b3 Mon Sep 17 00:00:00 2001 From: Nicholas Hammond Date: Sun, 18 Mar 2018 23:43:37 -0500 Subject: [PATCH 5/9] Spaces to Tabs + Tracking Observer Pattern --- diana/encoding.py | 246 +++--- diana/enumerations.py | 190 ++--- diana/object_update.py | 1744 ++++++++++++++++++++-------------------- diana/packet.py | 1328 +++++++++++++++--------------- diana/socket.py | 32 +- diana/tracking.py | 62 +- 6 files changed, 1804 insertions(+), 1798 deletions(-) diff --git a/diana/encoding.py b/diana/encoding.py index 37aa8d5..b06b925 100644 --- a/diana/encoding.py +++ b/diana/encoding.py @@ -3,167 +3,167 @@ ENCODERS = {} def struct_encoder_for(char): - format_expr = "<{}".format(char) - def st_encode(fmt, data): - if not data: - raise ValueError("Not enough data") - return (struct.pack(format_expr, data[0]) + - encode(fmt[1:], data[1:])) - return st_encode + format_expr = "<{}".format(char) + def st_encode(fmt, data): + if not data: + raise ValueError("Not enough data") + return (struct.pack(format_expr, data[0]) + + encode(fmt[1:], data[1:])) + return st_encode for base_format in 'bBiIf': - ENCODERS[base_format] = struct_encoder_for(base_format) + ENCODERS[base_format] = struct_encoder_for(base_format) ENCODERS['s'] = struct_encoder_for('h') ENCODERS['S'] = struct_encoder_for('H') def encode_unicode_string(fmt, data): - subject = data[0] - block = subject.encode('utf-16le') - output = struct.pack(':l - "6.3" : ('unknown-5','I'), - "6.4" : ('beacon-creature-type','I'), - "6.5" : ('beacon-mode','B') - }, - #ObjectType.player_ship_upgrade : { - # }, - ObjectType.torpedo : { - "1.1" : ('x','f'), - "1.2" : ('y','f'), - "1.3" : ('z','f'), - "1.4" : ('delta-x','f'), - "1.5" : ('delta-y','f'), - "1.6" : ('delta-z','f'), - "1.7" : ('unknown-1','I'), - "1.8" : ('ordnance-type','I'), + "6.1" : ('capital-ship-object-id','I'), + "6.2" : ('accent-color','f'), # colour >:l + "6.3" : ('unknown-5','I'), + "6.4" : ('beacon-creature-type','I'), + "6.5" : ('beacon-mode','B') + }, + #ObjectType.player_ship_upgrade : { + # }, + ObjectType.torpedo : { + "1.1" : ('x','f'), + "1.2" : ('y','f'), + "1.3" : ('z','f'), + "1.4" : ('delta-x','f'), + "1.5" : ('delta-y','f'), + "1.6" : ('delta-z','f'), + "1.7" : ('unknown-1','I'), + "1.8" : ('ordnance-type','I'), - "2.1" : ('unknown-2','B'), # there's two bytes of bitfields... - "2.2" : ('a','B'), # im lazy, these are unknown/unused fields - "2.3" : ('aa','B'), - "2.4" : ('aaa','B'), - "2.5" : ('aaaa','B'), - "2.6" : ('bbbb','B'), - "2.7" : ('bbb','B'), - "2.8" : ('bb','B') - }, - ObjectType.weapons_console : { - "1.1" : ('homing-count','B'), - "1.2" : ('nuke-count','B'), - "1.3" : ('mine-count','B'), - "1.4" : ('emp-count','B'), - "1.5" : ('pshock-count','B'), - "1.6" : ('beacon-count','B'), - "1.7" : ('probe-count','B'), - "1.8" : ('tag-count','B'), + "2.1" : ('unknown-2','B'), # there's two bytes of bitfields... + "2.2" : ('a','B'), # im lazy, these are unknown/unused fields + "2.3" : ('aa','B'), + "2.4" : ('aaa','B'), + "2.5" : ('aaaa','B'), + "2.6" : ('bbbb','B'), + "2.7" : ('bbb','B'), + "2.8" : ('bb','B') + }, + ObjectType.weapons_console : { + "1.1" : ('homing-count','B'), + "1.2" : ('nuke-count','B'), + "1.3" : ('mine-count','B'), + "1.4" : ('emp-count','B'), + "1.5" : ('pshock-count','B'), + "1.6" : ('beacon-count','B'), + "1.7" : ('probe-count','B'), + "1.8" : ('tag-count','B'), - "2.1" : ('tube-1-time','f'), - "2.2" : ('tube-2-time','f'), - "2.3" : ('tube-3-time','f'), - "2.4" : ('tube-4-time','f'), - "2.5" : ('tube-5-time','f'), - "2.6" : ('tube-6-time','f'), - "2.7" : ('tube-1-status','B'), - "2.8" : ('tube-2-status','B'), + "2.1" : ('tube-1-time','f'), + "2.2" : ('tube-2-time','f'), + "2.3" : ('tube-3-time','f'), + "2.4" : ('tube-4-time','f'), + "2.5" : ('tube-5-time','f'), + "2.6" : ('tube-6-time','f'), + "2.7" : ('tube-1-status','B'), + "2.8" : ('tube-2-status','B'), - "3.1" : ('tube-3-status','B'), - "3.2" : ('tube-4-status','B'), - "3.3" : ('tube-5-status','B'), - "3.4" : ('tube-6-status','B'), - "3.5" : ('tube-1-type','B'), - "3.6" : ('tube-2-type','B'), - "3.7" : ('tube-3-type','B'), - "3.8" : ('tube-4-type','B'), + "3.1" : ('tube-3-status','B'), + "3.2" : ('tube-4-status','B'), + "3.3" : ('tube-5-status','B'), + "3.4" : ('tube-6-status','B'), + "3.5" : ('tube-1-type','B'), + "3.6" : ('tube-2-type','B'), + "3.7" : ('tube-3-type','B'), + "3.8" : ('tube-4-type','B'), - "4.1" : ('tube-5-type','B'), - "4.2" : ('tube-6-type','B') - }, - ObjectType.asteroid : { - "1.1" : ('x','f'), - "1.2" : ('y','f'), - "1.3" : ('z','f'), - "1.4" : ('name','u'), - } - } + "4.1" : ('tube-5-type','B'), + "4.2" : ('tube-6-type','B') + }, + ObjectType.asteroid : { + "1.1" : ('x','f'), + "1.2" : ('y','f'), + "1.3" : ('z','f'), + "1.4" : ('name','u'), + } + } def itobf(x): - prefix = math.floor(x/8) + 1 - suffix = (x%8) + 1 - return str(prefix)+'.'+str(suffix) + prefix = math.floor(x/8) + 1 + suffix = (x%8) + 1 + return str(prefix)+'.'+str(suffix) player_ship_upgrades_list = [ 'infusion_p_coils', 'hydrogen_ram', 'tauron_focusers', - 'carapaction_coils', 'polyphasic_capacitors', 'cetrocite_crystals', 'lateral_array', - 'ecm_starpulse', 'double_agent', 'wartime_production', 'infusion_p_coils_perm', - 'protonic_verniers', 'tauron_focusers_perm', 'regenerative_pau_grids', - 'veteran_damcon_teams', 'cetrocite_heatsinks', 'tachyon_scanners', - 'gridscan_overload', 'override_authorization', 'resupply_imperatives', - 'patrol_group', 'fast_supply', 'vanguard_refit_helm', 'vanguard_refit_weap', - 'vanguard_refit_comm', 'vanguard_refit_station', 'vanguard_refit_eng', - 'vanguard_refit_systems' ] # so many oh my g*sh + 'carapaction_coils', 'polyphasic_capacitors', 'cetrocite_crystals', 'lateral_array', + 'ecm_starpulse', 'double_agent', 'wartime_production', 'infusion_p_coils_perm', + 'protonic_verniers', 'tauron_focusers_perm', 'regenerative_pau_grids', + 'veteran_damcon_teams', 'cetrocite_heatsinks', 'tachyon_scanners', + 'gridscan_overload', 'override_authorization', 'resupply_imperatives', + 'patrol_group', 'fast_supply', 'vanguard_refit_helm', 'vanguard_refit_weap', + 'vanguard_refit_comm', 'vanguard_refit_station', 'vanguard_refit_eng', + 'vanguard_refit_systems' ] # so many oh my g*sh ply_shp_upgrd = {} i=0 for field,ftype in (('active_','B'),('count_','B'),('time_','S')): - for upgrade in player_ship_upgrades_list: - ply_shp_upgrd[itobf(i)] = (field+upgrade,ftype) - i+=1 + for upgrade in player_ship_upgrades_list: + ply_shp_upgrd[itobf(i)] = (field+upgrade,ftype) + i+=1 # no way in hell i was writing that out one-by-one. 84 bitfields! Object_Properties[ObjectType.player_ship_upgrade] = ply_shp_upgrd @@ -320,604 +320,604 @@ def itobf(x): Object_Properties[ObjectType.blackhole] = Object_Properties[ObjectType.asteroid] def decode_obj_update_packet(packet): - entries = [] - while packet: - update_type = packet[0] - obj = {} - if update_type == 0x00: - break - if ObjectType(update_type) in Object_Properties.keys(): - properties = Object_Properties[ObjectType(update_type)] - fieldcount = max([int(fk.split('.')[0]) for fk in properties.keys()]) - unpacked = list(unpack('BIfbs*'.replace('fbs','B'*fieldcount),packet)) - # _id = unpacked[0] - # oid = unpacked[1] - # fields_k = unpacked[1+k] - # packet = unpacked[2+fieldcount] - obj['object'] = unpacked[1] # oid - obj['type'] = ObjectType(update_type) - for fdotb in properties: - prop = properties[fdotb] - f,b = [int(k) for k in fdotb.split(".")] # "byte.bit" -> (byte, bit) - propname, proptype = prop - if unpacked[1+f] & 2**(b-1): # field_f & (bit b) - value,pktremaining = unpack( - '%s*'%proptype, unpacked[2+fieldcount] - ) - obj[propname] = value - unpacked[2+fieldcount] = pktremaining - packet = unpacked[2+fieldcount] - else: - raise ValueError('Unknown object type {}'.format(update_type)) - entries.append(obj) - return entries + entries = [] + while packet: + update_type = packet[0] + obj = {} + if update_type == 0x00: + break + if ObjectType(update_type) in Object_Properties.keys(): + properties = Object_Properties[ObjectType(update_type)] + fieldcount = max([int(fk.split('.')[0]) for fk in properties.keys()]) + unpacked = list(unpack('BIfbs*'.replace('fbs','B'*fieldcount),packet)) + # _id = unpacked[0] + # oid = unpacked[1] + # fields_k = unpacked[1+k] + # packet = unpacked[2+fieldcount] + obj['object'] = unpacked[1] # oid + obj['type'] = ObjectType(update_type) + for fdotb in properties: + prop = properties[fdotb] + f,b = [int(k) for k in fdotb.split(".")] # "byte.bit" -> (byte, bit) + propname, proptype = prop + if unpacked[1+f] & 2**(b-1): # field_f & (bit b) + value,pktremaining = unpack( + '%s*'%proptype, unpacked[2+fieldcount] + ) + obj[propname] = value + unpacked[2+fieldcount] = pktremaining + packet = unpacked[2+fieldcount] + else: + raise ValueError('Unknown object type {}'.format(update_type)) + entries.append(obj) + return entries - # From here-on is the old object_update code + # From here-on is the old object_update code - while packet: - update_type = packet[0] - obj = {} - if update_type == 0x00: # end of ObjectUpdatePacket - break - elif update_type == ObjectType.player_vessel.value: # player ship - _id, oid, fields_1, fields_2, fields_3, fields_4, fields_5, fields_6, packet = unpack('BIBBBBBB*', packet) - obj['object'] = oid - obj['type'] = ObjectType.player_vessel - if fields_1 & 0x01: - obj['tgt-weapons'], packet = unpack('I*', packet) - if fields_1 & 0x02: - obj['impulse'], packet = unpack('f*', packet) - if fields_1 & 0x04: - obj['rudder'], packet = unpack('f*', packet) - if fields_1 & 0x08: - obj['top-speed'], packet = unpack('f*', packet) - if fields_1 & 0x10: - obj['turn-rate'], packet = unpack('f*', packet) - if fields_1 & 0x20: - ab, packet = unpack('B*', packet) - obj['auto-beams'] = bool(ab) - if fields_1 & 0x40: - obj['warp'], packet = unpack('B*', packet) - if fields_1 & 0x80: - obj['energy'], packet = unpack('f*', packet) + while packet: + update_type = packet[0] + obj = {} + if update_type == 0x00: # end of ObjectUpdatePacket + break + elif update_type == ObjectType.player_vessel.value: # player ship + _id, oid, fields_1, fields_2, fields_3, fields_4, fields_5, fields_6, packet = unpack('BIBBBBBB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.player_vessel + if fields_1 & 0x01: + obj['tgt-weapons'], packet = unpack('I*', packet) + if fields_1 & 0x02: + obj['impulse'], packet = unpack('f*', packet) + if fields_1 & 0x04: + obj['rudder'], packet = unpack('f*', packet) + if fields_1 & 0x08: + obj['top-speed'], packet = unpack('f*', packet) + if fields_1 & 0x10: + obj['turn-rate'], packet = unpack('f*', packet) + if fields_1 & 0x20: + ab, packet = unpack('B*', packet) + obj['auto-beams'] = bool(ab) + if fields_1 & 0x40: + obj['warp'], packet = unpack('B*', packet) + if fields_1 & 0x80: + obj['energy'], packet = unpack('f*', packet) - if fields_2 & 0x01: - obj['shields-state'], packet = unpack('S*', packet) - if fields_2 & 0x02: - packet = packet[4:] #now unknown - #obj['index'], packet = unpack('I*', packet) - if fields_2 & 0x04: - obj['vtype'], packet = unpack('I*', packet) - if fields_2 & 0x08: - obj['x'], packet = unpack('f*', packet) - if fields_2 & 0x10: - obj['y'], packet = unpack('f*', packet) - if fields_2 & 0x20: - obj['z'], packet = unpack('f*', packet) - if fields_2 & 0x40: - obj['pitch'], packet = unpack('f*', packet) - if fields_2 & 0x80: - obj['roll'], packet = unpack('f*', packet) + if fields_2 & 0x01: + obj['shields-state'], packet = unpack('S*', packet) + if fields_2 & 0x02: + packet = packet[4:] #now unknown + #obj['index'], packet = unpack('I*', packet) + if fields_2 & 0x04: + obj['vtype'], packet = unpack('I*', packet) + if fields_2 & 0x08: + obj['x'], packet = unpack('f*', packet) + if fields_2 & 0x10: + obj['y'], packet = unpack('f*', packet) + if fields_2 & 0x20: + obj['z'], packet = unpack('f*', packet) + if fields_2 & 0x40: + obj['pitch'], packet = unpack('f*', packet) + if fields_2 & 0x80: + obj['roll'], packet = unpack('f*', packet) - if fields_3 & 0x01: - obj['heading'], packet = unpack('f*', packet) - if fields_3 & 0x02: - obj['speed'], packet = unpack('f*', packet) - if fields_3 & 0x04: - obj['in-a-nebula'], packet = unpack('S*', packet) - if fields_3 & 0x08: - obj['name'], packet = unpack('u*', packet) - if fields_3 & 0x10: - obj['shields'], packet = unpack('f*', packet) - if fields_3 & 0x20: - obj['shields-max'], packet = unpack('f*', packet) - if fields_3 & 0x40: - obj['shields-aft'], packet = unpack('f*', packet) - if fields_3 & 0x80: - obj['shields-aft-max'], packet = unpack('f*', packet) + if fields_3 & 0x01: + obj['heading'], packet = unpack('f*', packet) + if fields_3 & 0x02: + obj['speed'], packet = unpack('f*', packet) + if fields_3 & 0x04: + obj['in-a-nebula'], packet = unpack('S*', packet) + if fields_3 & 0x08: + obj['name'], packet = unpack('u*', packet) + if fields_3 & 0x10: + obj['shields'], packet = unpack('f*', packet) + if fields_3 & 0x20: + obj['shields-max'], packet = unpack('f*', packet) + if fields_3 & 0x40: + obj['shields-aft'], packet = unpack('f*', packet) + if fields_3 & 0x80: + obj['shields-aft-max'], packet = unpack('f*', packet) - if fields_4 & 0x01: - obj['docked'], packet = unpack('I*', packet) - if fields_4 & 0x02: - red_alert, packet = unpack('B*', packet) - obj['red-alert'] = bool(red_alert) - if fields_4 & 0x04: - packet = packet[4:] # unknown - if fields_4 & 0x08: - ms, packet = unpack('B*', packet) - obj['main-view'] = MainView(ms) - if fields_4 & 0x10: - obj['beam-frequency'], packet = unpack('B*', packet) - if fields_4 & 0x20: - obj['coolant-avail'], packet = unpack('B*', packet) - if fields_4 & 0x40: - obj['tgt-science'], packet = unpack('I*', packet) - if fields_4 & 0x80: - obj['tgt-captain'], packet = unpack('I*', packet) + if fields_4 & 0x01: + obj['docked'], packet = unpack('I*', packet) + if fields_4 & 0x02: + red_alert, packet = unpack('B*', packet) + obj['red-alert'] = bool(red_alert) + if fields_4 & 0x04: + packet = packet[4:] # unknown + if fields_4 & 0x08: + ms, packet = unpack('B*', packet) + obj['main-view'] = MainView(ms) + if fields_4 & 0x10: + obj['beam-frequency'], packet = unpack('B*', packet) + if fields_4 & 0x20: + obj['coolant-avail'], packet = unpack('B*', packet) + if fields_4 & 0x40: + obj['tgt-science'], packet = unpack('I*', packet) + if fields_4 & 0x80: + obj['tgt-captain'], packet = unpack('I*', packet) - if fields_5 & 0x01: - dt, packet = unpack('B*', packet) - obj['drive-type'] = DriveType(dt) - if fields_5 & 0x02: - obj['tgt-scan'], packet = unpack('I*', packet) - if fields_5 & 0x04: - obj['scan-progress'], packet = unpack('f*', packet) - if fields_5 & 0x08: - rv, packet = unpack('B*', packet) - obj['reverse'] = bool(rv) - if fields_5 & 0x10: - packet = packet[4:] # float - if fields_5 & 0x20: - packet = packet[1:] - if fields_5 & 0x40: - packet = packet[4:] # int - if fields_5 & 0x80: - obj['ship-index?'], packet = unpack('B*', packet) + if fields_5 & 0x01: + dt, packet = unpack('B*', packet) + obj['drive-type'] = DriveType(dt) + if fields_5 & 0x02: + obj['tgt-scan'], packet = unpack('I*', packet) + if fields_5 & 0x04: + obj['scan-progress'], packet = unpack('f*', packet) + if fields_5 & 0x08: + rv, packet = unpack('B*', packet) + obj['reverse'] = bool(rv) + if fields_5 & 0x10: + packet = packet[4:] # float + if fields_5 & 0x20: + packet = packet[1:] + if fields_5 & 0x40: + packet = packet[4:] # int + if fields_5 & 0x80: + obj['ship-index?'], packet = unpack('B*', packet) - if fields_6 & 0x01: - obj['capital-ship-obj-id'], packet = unpack('I*', packet) - if fields_6 & 0x02: - obj['accent-colour'], packet = unpack('f*', packet) - if fields_6 & 0x04: - packet = packet[4:] # unknown - if fields_6 & 0x08: - obj['beacon-creature-type'], packet = unpack('I*', packet) - if fields_6 & 0x10: - obj['beacon-mode'], packet = unpack('B*', packet) - if fields_6 & 0x20: - raise ValueError('Unknown data keys for player vessel') - if fields_6 & 0x40: - raise ValueError('Unknown data keys for player vessel') - if fields_6 & 0x80: - raise ValueError('Unknown data keys for player vessel') - elif update_type == ObjectType.weapons_console.value: # weapons console - _id, oid, fields_1, fields_2, fields_3, packet = unpack('BIBBB*', packet) - obj['object'] = oid - obj['type'] = ObjectType.weapons_console - if fields_1 & 0x01: # TODO: use the enum here - obj['store-missile'], packet = unpack('B*', packet) - if fields_1 & 0x02: - obj['store-nuke'], packet = unpack('B*', packet) - if fields_1 & 0x04: - obj['store-mine'], packet = unpack('B*', packet) - if fields_1 & 0x08: - obj['store-emp'], packet = unpack('B*', packet) - if fields_1 & 0x10: - packet = packet[1:] - if fields_1 & 0x20: - obj['load-time-0'], packet = unpack('f*', packet) - if fields_1 & 0x40: - obj['load-time-1'], packet = unpack('f*', packet) - if fields_1 & 0x80: - obj['load-time-2'], packet = unpack('f*', packet) - if fields_2 & 0x01: - obj['load-time-3'], packet = unpack('f*', packet) - if fields_2 & 0x02: - obj['load-time-4'], packet = unpack('f*', packet) - if fields_2 & 0x04: - obj['load-time-5'], packet = unpack('f*', packet) - if fields_2 & 0x08: - ts, packet = unpack('B*', packet) - obj['status-0'] = TubeStatus(ts) - if fields_2 & 0x10: - ts, packet = unpack('B*', packet) - obj['status-1'] = TubeStatus(ts) - if fields_2 & 0x20: - ts, packet = unpack('B*', packet) - obj['status-2'] = TubeStatus(ts) - if fields_2 & 0x40: - ts, packet = unpack('B*', packet) - obj['status-3'] = TubeStatus(ts) - if fields_2 & 0x80: - ts, packet = unpack('B*', packet) - obj['status-4'] = TubeStatus(ts) - if fields_3 & 0x01: - ts, packet = unpack('B*', packet) - obj['status-5'] = TubeStatus(ts) - if fields_3 & 0x02: - ot, packet = unpack('B*', packet) - obj['contents-0'] = OrdnanceType(ot) - if fields_3 & 0x04: - ot, packet = unpack('B*', packet) - obj['contents-1'] = OrdnanceType(ot) - if fields_3 & 0x08: - ot, packet = unpack('B*', packet) - obj['contents-2'] = OrdnanceType(ot) - if fields_3 & 0x10: - ot, packet = unpack('B*', packet) - obj['contents-3'] = OrdnanceType(ot) - if fields_3 & 0x20: - ot, packet = unpack('B*', packet) - obj['contents-4'] = OrdnanceType(ot) - if fields_3 & 0x40: - ot, packet = unpack('B*', packet) - obj['contents-5'] = OrdnanceType(ot) - if fields_3 & 0x80: - raise ValueError('Unknown fields for weapons console') - elif update_type == ObjectType.engineering_console.value: # engineering console - _id, oid, fields_heat, fields_enrg, fields_coolant, fields_unk, packet = unpack('BIBBBB*', packet) - obj['object'] = oid - obj['type'] = ObjectType.engineering_console - if fields_unk: - raise ValueError('Undecodable fields in engineering status') - systems = (('beams', 0x01), - ('torps', 0x02), - ('sensors', 0x04), - ('maneuvering', 0x08), - ('impulse', 0x10), - ('warp', 0x20), - ('shields', 0x40), - ('shields-aft', 0x80)) - types = (('heat', fields_heat, 'f'), - ('energy', fields_enrg, 'f'), - ('coolant', fields_coolant, 'B')) - for status, mask, fmt in types: - for syst, flag in systems: - if fields_heat & flag: - obj['{}-{}'.format(status, syst)], packet = unpack(fmt + '*', packet) - elif update_type == ObjectType.player_ship_upgrade.value: # player ship upgrades - _id, oid, fields, packet = unpack('BIB*', packet) - obj['object'] = oid - obj['type'] = ObjectType.player_ship_upgrade - if fields & 0x01: - obj['x'], packet = unpack('f*', packet) - if fields & 0x02: - obj['y'], packet = unpack('f*', packet) - if fields & 0x04: - obj['z'], packet = unpack('f*', packet) - if fields & 0x08: - obj['upgrade'], packet = unpack('I*', packet) - if fields & 0x10: - packet = packet[4:] - if fields & 0x20: - packet = packet[4:] - if fields & 0x40: - packet = packet[4:] - if fields & 0x80: - packet = packet[4:] - elif update_type == ObjectType.other_ship.value: # NPC ship - _id, oid, fields_1, fields_2, fields_3, fields_4, fields_5, fields_6, packet = unpack('BIBBBBBB*', packet) - obj['object'] = oid - obj['type'] = ObjectType.other_ship - if fields_1 & 0x01: - obj['name'], packet = unpack('u*', packet) - if fields_1 & 0x02: - packet = packet[4:] - if fields_1 & 0x04: - obj['rudder'], packet = unpack('f*', packet) - if fields_1 & 0x08: - obj['max-impulse'], packet = unpack('f*', packet) - if fields_1 & 0x10: - obj['max-turn-rate'], packet = unpack('f*', packet) - if fields_1 & 0x20: - fef, packet = unpack('I*', packet) - obj['iff-friendly'] = not bool(fef) - if fields_1 & 0x40: - obj['vtype'], packet = unpack('I*', packet) - if fields_1 & 0x80: - obj['x'], packet = unpack('f*', packet) - if fields_2 & 0x01: - obj['y'], packet = unpack('f*', packet) - if fields_2 & 0x02: - obj['z'], packet = unpack('f*', packet) - if fields_2 & 0x04: - obj['pitch'], packet = unpack('f*', packet) - if fields_2 & 0x08: - obj['roll'], packet = unpack('f*', packet) - if fields_2 & 0x10: - obj['heading'], packet = unpack('f*', packet) - if fields_2 & 0x20: - obj['speed'], packet = unpack('f*', packet) - if fields_2 & 0x40: - surr, packet = unpack('B*', packet) - obj['surrender'] = bool(surr) - if fields_2 & 0x80: - packet = packet[2:] - if fields_3 & 0x01: - obj['shields'], packet = unpack('f*', packet) - if fields_3 & 0x02: - obj['shields-max'], packet = unpack('f*', packet) - if fields_3 & 0x04: - obj['shields-aft'], packet = unpack('f*', packet) - if fields_3 & 0x08: - obj['shields-aft-max'], packet = unpack('f*', packet) - if fields_3 & 0x10: - packet = packet[2:] - if fields_3 & 0x20: - packet = packet[1:] - if fields_3 & 0x40: - elt, packet = unpack('I*', packet) - obj['elite'] = unscramble_elites(elt) - if fields_3 & 0x80: - elt, packet = unpack('I*', packet) - obj['elite-active'] = unscramble_elites(elt) - if fields_4 & 0x01: - scn, packet = unpack('I*', packet) - obj['scanned'] = bool(scn) - if fields_4 & 0x02: - obj['iff-side'], packet = unpack('I*', packet) - if fields_4 & 0x04: - packet = packet[4:] - if fields_4 & 0x08: - packet = packet[1:] - if fields_4 & 0x10: - packet = packet[1:] - if fields_4 & 0x20: - packet = packet[1:] - if fields_4 & 0x40: - packet = packet[1:] - if fields_4 & 0x80: - packet = packet[4:] - if fields_5 & 0x01: - packet = packet[4:] - if fields_5 & 0x02: - packet = packet[4:] - if fields_5 & 0x04: - obj['damage-beams'], packet = unpack('f*', packet) - if fields_5 & 0x08: - obj['damage-tubes'], packet = unpack('f*', packet) - if fields_5 & 0x10: - obj['damage-sensors'], packet = unpack('f*', packet) - if fields_5 & 0x20: - obj['damage-maneuvering'], packet = unpack('f*', packet) - if fields_5 & 0x40: - obj['damage-impulse'], packet = unpack('f*', packet) - if fields_5 & 0x80: - obj['damage-warp'], packet = unpack('f*', packet) - if fields_6 & 0x01: - obj['damage-shields'], packet = unpack('f*', packet) - if fields_6 & 0x02: - obj['damage-shields'], packet = unpack('f*', packet) - if fields_6 & 0x04: - obj['shields-0'], packet = unpack('f*', packet) - if fields_6 & 0x08: - obj['shields-1'], packet = unpack('f*', packet) - if fields_6 & 0x10: - obj['shields-2'], packet = unpack('f*', packet) - if fields_6 & 0x20: - obj['shields-3'], packet = unpack('f*', packet) - if fields_6 & 0x40: - obj['shields-4'], packet = unpack('f*', packet) - if fields_6 & 0x80: - raise ValueError('Unknown data key for NPC') - elif update_type == ObjectType.base.value: # base - _id, oid, fields_1, fields_2, packet = unpack('BIBB*', packet) - obj['object'] = oid - obj['type'] = ObjectType.base - if fields_1 & 0x01: - obj['name'], packet = unpack('u*', packet) - if fields_1 & 0x02: - obj['shields'], packet = unpack('f*', packet) - if fields_1 & 0x04: - obj['shields-aft'], packet = unpack('f*', packet) - if fields_1 & 0x08: - obj['index'], packet = unpack('I*', packet) - if fields_1 & 0x10: - obj['vtype'], packet = unpack('I*', packet) - if fields_1 & 0x20: - obj['x'], packet = unpack('f*', packet) - if fields_1 & 0x40: - obj['y'], packet = unpack('f*', packet) - if fields_1 & 0x80: - obj['z'], packet = unpack('f*', packet) - if fields_2 & 0x01: - packet = packet[4:] - if fields_2 & 0x02: - packet = packet[4:] - if fields_2 & 0x04: - packet = packet[4:] - if fields_2 & 0x08: - packet = packet[4:] - if fields_2 & 0x10: - packet = packet[1:] - if fields_2 & 0x20: - packet = packet[1:] - if fields_2 & 0xc0: - raise ValueError('Unknown data keys for base') - elif update_type == ObjectType.mine.value: # mine - _id, oid, fields, packet = unpack('BIB*', packet) - obj['object'] = oid - obj['type'] = ObjectType.mine - if fields & 0x01: - obj['x'], packet = unpack('f*', packet) - if fields & 0x02: - obj['y'], packet = unpack('f*', packet) - if fields & 0x04: - obj['z'], packet = unpack('f*', packet) - if fields & 0x08: - packet = packet[4:] - if fields & 0x10: - packet = packet[4:] - if fields & 0x20: - packet = packet[4:] - if fields & 0x40: - packet = packet[4:] - if fields & 0x80: - packet = packet[4:] - elif update_type == ObjectType.anomaly.value: # anomaly - _id, oid, fields, packet = unpack('BIB*', packet) - obj['object'] = oid - obj['type'] = ObjectType.anomaly - if fields & 0x01: - obj['x'], packet = unpack('f*', packet) - if fields & 0x02: - obj['y'], packet = unpack('f*', packet) - if fields & 0x04: - obj['z'], packet = unpack('f*', packet) - if fields & 0x08: - obj['upgrade'], packet = unpack('I*', packet) - if fields & 0x10: - packet = packet[4:] - if fields & 0x20: - packet = packet[4:] - if fields & 0x40: - packet = packet[4:] - if fields & 0x80: - packet = packet[4:] - elif update_type == 0x09: # unused - _id, oid, fields, packet = unpack('BIB*', packet) - obj['object'] = oid - obj['type'] = ObjectType.anomaly - if fields & 0x01: - obj['x'], packet = unpack('f*', packet) - if fields & 0x02: - obj['y'], packet = unpack('f*', packet) - if fields & 0x04: - obj['z'], packet = unpack('f*', packet) - if fields & 0x08: - obj['name'], packet = unpack('u*', packet) - if fields & 0x10: - packet = packet[4:] - if fields & 0x20: - packet = packet[4:] - if fields & 0x40: - packet = packet[4:] - if fields & 0x80: - packet = packet[4:] - elif update_type == ObjectType.nebula.value: # nebula - _id, oid, fields, packet = unpack('BIB*', packet) - obj['object'] = oid - obj['type'] = ObjectType.nebula - if fields & 0x01: - obj['x'], packet = unpack('f*', packet) - if fields & 0x02: - obj['y'], packet = unpack('f*', packet) - if fields & 0x04: - obj['z'], packet = unpack('f*', packet) - if fields & 0x08: - obj['red'], packet = unpack('f*', packet) - if fields & 0x10: - obj['green'], packet = unpack('f*', packet) - if fields & 0x20: - obj['blue'], packet = unpack('f*', packet) - if fields & 0x40: - packet = packet[4:] - if fields & 0x80: - packet = packet[4:] - elif update_type == ObjectType.torpedo.value: # torpedo - _id, oid, fields, packet = unpack('BIB*', packet) - obj['object'] = oid - obj['type'] = ObjectType.torpedo - if fields & 0x01: - obj['x'], packet = unpack('f*', packet) - if fields & 0x02: - obj['y'], packet = unpack('f*', packet) - if fields & 0x04: - obj['z'], packet = unpack('f*', packet) - if fields & 0x08: - packet = packet[4:] - if fields & 0x10: - packet = packet[4:] - if fields & 0x20: - packet = packet[4:] - if fields & 0x40: - packet = packet[4:] - if fields & 0x80: - packet = packet[4:] - elif update_type == ObjectType.blackhole.value: # black hole - _id, oid, fields, packet = unpack('BIB*', packet) - obj['object'] = oid - obj['type'] = ObjectType.blackhole - if fields & 0x01: - obj['x'], packet = unpack('f*', packet) - if fields & 0x02: - obj['y'], packet = unpack('f*', packet) - if fields & 0x04: - obj['z'], packet = unpack('f*', packet) - if fields & 0x08: - packet = packet[4:] - if fields & 0x10: - packet = packet[4:] - if fields & 0x20: - packet = packet[4:] - if fields & 0x40: - packet = packet[4:] - if fields & 0x80: - packet = packet[4:] - elif update_type == ObjectType.asteroid.value: # asteroid - _id, oid, fields, packet = unpack('BIB*', packet) - obj['object'] = oid - obj['type'] = ObjectType.asteroid - if fields & 0x01: - obj['x'], packet = unpack('f*', packet) - if fields & 0x02: - obj['y'], packet = unpack('f*', packet) - if fields & 0x04: - obj['z'], packet = unpack('f*', packet) - if fields & 0x08: - packet = packet[4:] - if fields & 0x10: - packet = packet[4:] - if fields & 0x20: - packet = packet[4:] - if fields & 0x40: - packet = packet[4:] - if fields & 0x80: - packet = packet[4:] - elif update_type == ObjectType.mesh.value: # generic mesh - _id, oid, fields, packet = unpack('BIB*', packet) - obj['object'] = oid - obj['type'] = ObjectType.mesh - if fields & 0x01: - obj['x'], packet = unpack('f*', packet) - if fields & 0x02: - obj['y'], packet = unpack('f*', packet) - if fields & 0x04: - obj['z'], packet = unpack('f*', packet) - if fields & 0x08: - packet = packet[4:] - if fields & 0x10: - packet = packet[4:] - if fields & 0x20: - packet = packet[4:] - if fields & 0x40: - packet = packet[4:] - if fields & 0x80: - packet = packet[4:] - elif update_type == ObjectType.creature.value: # creature - _id, oid, fields, packet = unpack('BIB*', packet) - obj['object'] = oid - obj['type'] = ObjectType.monster - if fields & 0x01: - obj['x'], packet = unpack('f*', packet) - if fields & 0x02: - obj['y'], packet = unpack('f*', packet) - if fields & 0x04: - obj['z'], packet = unpack('f*', packet) - if fields & 0x08: - obj['name'], packet = unpack('u*', packet) - if fields & 0x10: - packet = packet[4:] - if fields & 0x20: - packet = packet[4:] - if fields & 0x40: - packet = packet[4:] - if fields & 0x80: - packet = packet[4:] - elif update_type == ObjectType.drone.value: # drone - _id, oid, fields_1, fields_2, packet = unpack('BIBB*', packet) - obj['object'] = oid - obj['type'] = ObjectType.drone - if fields_1 & 0x01: - packet = packet[4:] - if fields_1 & 0x02: - obj['x'], packet = unpack('f*', packet) - if fields_1 & 0x04: - packet = packet[4:] - if fields_1 & 0x08: - obj['z'], packet = unpack('f*', packet) - if fields_1 & 0x10: - packet = packet[4:] - if fields_1 & 0x20: - obj['y'], packet = unpack('f*', packet) - if fields_1 & 0x40: - obj['heading'], packet = unpack('f*', packet) - if fields_1 & 0x80: - packet = packet[4:] - if fields_2: - raise ValueError('Unknown data keys for drone') - else: - raise ValueError('Unknown object type {}'.format(update_type)) - entries.append(obj) - return entries + if fields_6 & 0x01: + obj['capital-ship-obj-id'], packet = unpack('I*', packet) + if fields_6 & 0x02: + obj['accent-colour'], packet = unpack('f*', packet) + if fields_6 & 0x04: + packet = packet[4:] # unknown + if fields_6 & 0x08: + obj['beacon-creature-type'], packet = unpack('I*', packet) + if fields_6 & 0x10: + obj['beacon-mode'], packet = unpack('B*', packet) + if fields_6 & 0x20: + raise ValueError('Unknown data keys for player vessel') + if fields_6 & 0x40: + raise ValueError('Unknown data keys for player vessel') + if fields_6 & 0x80: + raise ValueError('Unknown data keys for player vessel') + elif update_type == ObjectType.weapons_console.value: # weapons console + _id, oid, fields_1, fields_2, fields_3, packet = unpack('BIBBB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.weapons_console + if fields_1 & 0x01: # TODO: use the enum here + obj['store-missile'], packet = unpack('B*', packet) + if fields_1 & 0x02: + obj['store-nuke'], packet = unpack('B*', packet) + if fields_1 & 0x04: + obj['store-mine'], packet = unpack('B*', packet) + if fields_1 & 0x08: + obj['store-emp'], packet = unpack('B*', packet) + if fields_1 & 0x10: + packet = packet[1:] + if fields_1 & 0x20: + obj['load-time-0'], packet = unpack('f*', packet) + if fields_1 & 0x40: + obj['load-time-1'], packet = unpack('f*', packet) + if fields_1 & 0x80: + obj['load-time-2'], packet = unpack('f*', packet) + if fields_2 & 0x01: + obj['load-time-3'], packet = unpack('f*', packet) + if fields_2 & 0x02: + obj['load-time-4'], packet = unpack('f*', packet) + if fields_2 & 0x04: + obj['load-time-5'], packet = unpack('f*', packet) + if fields_2 & 0x08: + ts, packet = unpack('B*', packet) + obj['status-0'] = TubeStatus(ts) + if fields_2 & 0x10: + ts, packet = unpack('B*', packet) + obj['status-1'] = TubeStatus(ts) + if fields_2 & 0x20: + ts, packet = unpack('B*', packet) + obj['status-2'] = TubeStatus(ts) + if fields_2 & 0x40: + ts, packet = unpack('B*', packet) + obj['status-3'] = TubeStatus(ts) + if fields_2 & 0x80: + ts, packet = unpack('B*', packet) + obj['status-4'] = TubeStatus(ts) + if fields_3 & 0x01: + ts, packet = unpack('B*', packet) + obj['status-5'] = TubeStatus(ts) + if fields_3 & 0x02: + ot, packet = unpack('B*', packet) + obj['contents-0'] = OrdnanceType(ot) + if fields_3 & 0x04: + ot, packet = unpack('B*', packet) + obj['contents-1'] = OrdnanceType(ot) + if fields_3 & 0x08: + ot, packet = unpack('B*', packet) + obj['contents-2'] = OrdnanceType(ot) + if fields_3 & 0x10: + ot, packet = unpack('B*', packet) + obj['contents-3'] = OrdnanceType(ot) + if fields_3 & 0x20: + ot, packet = unpack('B*', packet) + obj['contents-4'] = OrdnanceType(ot) + if fields_3 & 0x40: + ot, packet = unpack('B*', packet) + obj['contents-5'] = OrdnanceType(ot) + if fields_3 & 0x80: + raise ValueError('Unknown fields for weapons console') + elif update_type == ObjectType.engineering_console.value: # engineering console + _id, oid, fields_heat, fields_enrg, fields_coolant, fields_unk, packet = unpack('BIBBBB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.engineering_console + if fields_unk: + raise ValueError('Undecodable fields in engineering status') + systems = (('beams', 0x01), + ('torps', 0x02), + ('sensors', 0x04), + ('maneuvering', 0x08), + ('impulse', 0x10), + ('warp', 0x20), + ('shields', 0x40), + ('shields-aft', 0x80)) + types = (('heat', fields_heat, 'f'), + ('energy', fields_enrg, 'f'), + ('coolant', fields_coolant, 'B')) + for status, mask, fmt in types: + for syst, flag in systems: + if fields_heat & flag: + obj['{}-{}'.format(status, syst)], packet = unpack(fmt + '*', packet) + elif update_type == ObjectType.player_ship_upgrade.value: # player ship upgrades + _id, oid, fields, packet = unpack('BIB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.player_ship_upgrade + if fields & 0x01: + obj['x'], packet = unpack('f*', packet) + if fields & 0x02: + obj['y'], packet = unpack('f*', packet) + if fields & 0x04: + obj['z'], packet = unpack('f*', packet) + if fields & 0x08: + obj['upgrade'], packet = unpack('I*', packet) + if fields & 0x10: + packet = packet[4:] + if fields & 0x20: + packet = packet[4:] + if fields & 0x40: + packet = packet[4:] + if fields & 0x80: + packet = packet[4:] + elif update_type == ObjectType.other_ship.value: # NPC ship + _id, oid, fields_1, fields_2, fields_3, fields_4, fields_5, fields_6, packet = unpack('BIBBBBBB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.other_ship + if fields_1 & 0x01: + obj['name'], packet = unpack('u*', packet) + if fields_1 & 0x02: + packet = packet[4:] + if fields_1 & 0x04: + obj['rudder'], packet = unpack('f*', packet) + if fields_1 & 0x08: + obj['max-impulse'], packet = unpack('f*', packet) + if fields_1 & 0x10: + obj['max-turn-rate'], packet = unpack('f*', packet) + if fields_1 & 0x20: + fef, packet = unpack('I*', packet) + obj['iff-friendly'] = not bool(fef) + if fields_1 & 0x40: + obj['vtype'], packet = unpack('I*', packet) + if fields_1 & 0x80: + obj['x'], packet = unpack('f*', packet) + if fields_2 & 0x01: + obj['y'], packet = unpack('f*', packet) + if fields_2 & 0x02: + obj['z'], packet = unpack('f*', packet) + if fields_2 & 0x04: + obj['pitch'], packet = unpack('f*', packet) + if fields_2 & 0x08: + obj['roll'], packet = unpack('f*', packet) + if fields_2 & 0x10: + obj['heading'], packet = unpack('f*', packet) + if fields_2 & 0x20: + obj['speed'], packet = unpack('f*', packet) + if fields_2 & 0x40: + surr, packet = unpack('B*', packet) + obj['surrender'] = bool(surr) + if fields_2 & 0x80: + packet = packet[2:] + if fields_3 & 0x01: + obj['shields'], packet = unpack('f*', packet) + if fields_3 & 0x02: + obj['shields-max'], packet = unpack('f*', packet) + if fields_3 & 0x04: + obj['shields-aft'], packet = unpack('f*', packet) + if fields_3 & 0x08: + obj['shields-aft-max'], packet = unpack('f*', packet) + if fields_3 & 0x10: + packet = packet[2:] + if fields_3 & 0x20: + packet = packet[1:] + if fields_3 & 0x40: + elt, packet = unpack('I*', packet) + obj['elite'] = unscramble_elites(elt) + if fields_3 & 0x80: + elt, packet = unpack('I*', packet) + obj['elite-active'] = unscramble_elites(elt) + if fields_4 & 0x01: + scn, packet = unpack('I*', packet) + obj['scanned'] = bool(scn) + if fields_4 & 0x02: + obj['iff-side'], packet = unpack('I*', packet) + if fields_4 & 0x04: + packet = packet[4:] + if fields_4 & 0x08: + packet = packet[1:] + if fields_4 & 0x10: + packet = packet[1:] + if fields_4 & 0x20: + packet = packet[1:] + if fields_4 & 0x40: + packet = packet[1:] + if fields_4 & 0x80: + packet = packet[4:] + if fields_5 & 0x01: + packet = packet[4:] + if fields_5 & 0x02: + packet = packet[4:] + if fields_5 & 0x04: + obj['damage-beams'], packet = unpack('f*', packet) + if fields_5 & 0x08: + obj['damage-tubes'], packet = unpack('f*', packet) + if fields_5 & 0x10: + obj['damage-sensors'], packet = unpack('f*', packet) + if fields_5 & 0x20: + obj['damage-maneuvering'], packet = unpack('f*', packet) + if fields_5 & 0x40: + obj['damage-impulse'], packet = unpack('f*', packet) + if fields_5 & 0x80: + obj['damage-warp'], packet = unpack('f*', packet) + if fields_6 & 0x01: + obj['damage-shields'], packet = unpack('f*', packet) + if fields_6 & 0x02: + obj['damage-shields'], packet = unpack('f*', packet) + if fields_6 & 0x04: + obj['shields-0'], packet = unpack('f*', packet) + if fields_6 & 0x08: + obj['shields-1'], packet = unpack('f*', packet) + if fields_6 & 0x10: + obj['shields-2'], packet = unpack('f*', packet) + if fields_6 & 0x20: + obj['shields-3'], packet = unpack('f*', packet) + if fields_6 & 0x40: + obj['shields-4'], packet = unpack('f*', packet) + if fields_6 & 0x80: + raise ValueError('Unknown data key for NPC') + elif update_type == ObjectType.base.value: # base + _id, oid, fields_1, fields_2, packet = unpack('BIBB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.base + if fields_1 & 0x01: + obj['name'], packet = unpack('u*', packet) + if fields_1 & 0x02: + obj['shields'], packet = unpack('f*', packet) + if fields_1 & 0x04: + obj['shields-aft'], packet = unpack('f*', packet) + if fields_1 & 0x08: + obj['index'], packet = unpack('I*', packet) + if fields_1 & 0x10: + obj['vtype'], packet = unpack('I*', packet) + if fields_1 & 0x20: + obj['x'], packet = unpack('f*', packet) + if fields_1 & 0x40: + obj['y'], packet = unpack('f*', packet) + if fields_1 & 0x80: + obj['z'], packet = unpack('f*', packet) + if fields_2 & 0x01: + packet = packet[4:] + if fields_2 & 0x02: + packet = packet[4:] + if fields_2 & 0x04: + packet = packet[4:] + if fields_2 & 0x08: + packet = packet[4:] + if fields_2 & 0x10: + packet = packet[1:] + if fields_2 & 0x20: + packet = packet[1:] + if fields_2 & 0xc0: + raise ValueError('Unknown data keys for base') + elif update_type == ObjectType.mine.value: # mine + _id, oid, fields, packet = unpack('BIB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.mine + if fields & 0x01: + obj['x'], packet = unpack('f*', packet) + if fields & 0x02: + obj['y'], packet = unpack('f*', packet) + if fields & 0x04: + obj['z'], packet = unpack('f*', packet) + if fields & 0x08: + packet = packet[4:] + if fields & 0x10: + packet = packet[4:] + if fields & 0x20: + packet = packet[4:] + if fields & 0x40: + packet = packet[4:] + if fields & 0x80: + packet = packet[4:] + elif update_type == ObjectType.anomaly.value: # anomaly + _id, oid, fields, packet = unpack('BIB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.anomaly + if fields & 0x01: + obj['x'], packet = unpack('f*', packet) + if fields & 0x02: + obj['y'], packet = unpack('f*', packet) + if fields & 0x04: + obj['z'], packet = unpack('f*', packet) + if fields & 0x08: + obj['upgrade'], packet = unpack('I*', packet) + if fields & 0x10: + packet = packet[4:] + if fields & 0x20: + packet = packet[4:] + if fields & 0x40: + packet = packet[4:] + if fields & 0x80: + packet = packet[4:] + elif update_type == 0x09: # unused + _id, oid, fields, packet = unpack('BIB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.anomaly + if fields & 0x01: + obj['x'], packet = unpack('f*', packet) + if fields & 0x02: + obj['y'], packet = unpack('f*', packet) + if fields & 0x04: + obj['z'], packet = unpack('f*', packet) + if fields & 0x08: + obj['name'], packet = unpack('u*', packet) + if fields & 0x10: + packet = packet[4:] + if fields & 0x20: + packet = packet[4:] + if fields & 0x40: + packet = packet[4:] + if fields & 0x80: + packet = packet[4:] + elif update_type == ObjectType.nebula.value: # nebula + _id, oid, fields, packet = unpack('BIB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.nebula + if fields & 0x01: + obj['x'], packet = unpack('f*', packet) + if fields & 0x02: + obj['y'], packet = unpack('f*', packet) + if fields & 0x04: + obj['z'], packet = unpack('f*', packet) + if fields & 0x08: + obj['red'], packet = unpack('f*', packet) + if fields & 0x10: + obj['green'], packet = unpack('f*', packet) + if fields & 0x20: + obj['blue'], packet = unpack('f*', packet) + if fields & 0x40: + packet = packet[4:] + if fields & 0x80: + packet = packet[4:] + elif update_type == ObjectType.torpedo.value: # torpedo + _id, oid, fields, packet = unpack('BIB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.torpedo + if fields & 0x01: + obj['x'], packet = unpack('f*', packet) + if fields & 0x02: + obj['y'], packet = unpack('f*', packet) + if fields & 0x04: + obj['z'], packet = unpack('f*', packet) + if fields & 0x08: + packet = packet[4:] + if fields & 0x10: + packet = packet[4:] + if fields & 0x20: + packet = packet[4:] + if fields & 0x40: + packet = packet[4:] + if fields & 0x80: + packet = packet[4:] + elif update_type == ObjectType.blackhole.value: # black hole + _id, oid, fields, packet = unpack('BIB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.blackhole + if fields & 0x01: + obj['x'], packet = unpack('f*', packet) + if fields & 0x02: + obj['y'], packet = unpack('f*', packet) + if fields & 0x04: + obj['z'], packet = unpack('f*', packet) + if fields & 0x08: + packet = packet[4:] + if fields & 0x10: + packet = packet[4:] + if fields & 0x20: + packet = packet[4:] + if fields & 0x40: + packet = packet[4:] + if fields & 0x80: + packet = packet[4:] + elif update_type == ObjectType.asteroid.value: # asteroid + _id, oid, fields, packet = unpack('BIB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.asteroid + if fields & 0x01: + obj['x'], packet = unpack('f*', packet) + if fields & 0x02: + obj['y'], packet = unpack('f*', packet) + if fields & 0x04: + obj['z'], packet = unpack('f*', packet) + if fields & 0x08: + packet = packet[4:] + if fields & 0x10: + packet = packet[4:] + if fields & 0x20: + packet = packet[4:] + if fields & 0x40: + packet = packet[4:] + if fields & 0x80: + packet = packet[4:] + elif update_type == ObjectType.mesh.value: # generic mesh + _id, oid, fields, packet = unpack('BIB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.mesh + if fields & 0x01: + obj['x'], packet = unpack('f*', packet) + if fields & 0x02: + obj['y'], packet = unpack('f*', packet) + if fields & 0x04: + obj['z'], packet = unpack('f*', packet) + if fields & 0x08: + packet = packet[4:] + if fields & 0x10: + packet = packet[4:] + if fields & 0x20: + packet = packet[4:] + if fields & 0x40: + packet = packet[4:] + if fields & 0x80: + packet = packet[4:] + elif update_type == ObjectType.creature.value: # creature + _id, oid, fields, packet = unpack('BIB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.monster + if fields & 0x01: + obj['x'], packet = unpack('f*', packet) + if fields & 0x02: + obj['y'], packet = unpack('f*', packet) + if fields & 0x04: + obj['z'], packet = unpack('f*', packet) + if fields & 0x08: + obj['name'], packet = unpack('u*', packet) + if fields & 0x10: + packet = packet[4:] + if fields & 0x20: + packet = packet[4:] + if fields & 0x40: + packet = packet[4:] + if fields & 0x80: + packet = packet[4:] + elif update_type == ObjectType.drone.value: # drone + _id, oid, fields_1, fields_2, packet = unpack('BIBB*', packet) + obj['object'] = oid + obj['type'] = ObjectType.drone + if fields_1 & 0x01: + packet = packet[4:] + if fields_1 & 0x02: + obj['x'], packet = unpack('f*', packet) + if fields_1 & 0x04: + packet = packet[4:] + if fields_1 & 0x08: + obj['z'], packet = unpack('f*', packet) + if fields_1 & 0x10: + packet = packet[4:] + if fields_1 & 0x20: + obj['y'], packet = unpack('f*', packet) + if fields_1 & 0x40: + obj['heading'], packet = unpack('f*', packet) + if fields_1 & 0x80: + packet = packet[4:] + if fields_2: + raise ValueError('Unknown data keys for drone') + else: + raise ValueError('Unknown object type {}'.format(update_type)) + entries.append(obj) + return entries diff --git a/diana/packet.py b/diana/packet.py index d391edd..0211d29 100644 --- a/diana/packet.py +++ b/diana/packet.py @@ -6,882 +6,882 @@ from .enumerations import * def pack(fmt, *args): - return base_pack(fmt, args) + return base_pack(fmt, args) class SoftDecodeFailure(RuntimeError): - pass + pass PACKETS = {} def packet(n): - def wrapper(cls): - PACKETS[n] = cls - cls.packet_id = n - return cls - return wrapper + def wrapper(cls): + PACKETS[n] = cls + cls.packet_id = n + return cls + return wrapper class UndecodedPacket: - def __init__(self, packet_id, data): - self.packet_id = packet_id - self.data = data + def __init__(self, packet_id, data): + self.packet_id = packet_id + self.data = data - def encode(self): - return self.data + def encode(self): + return self.data - @classmethod - def decode(cls, data): - return cls(0, data) + @classmethod + def decode(cls, data): + return cls(0, data) - def __str__(self): - return "".format(self.packet_id, self.data) + def __str__(self): + return "".format(self.packet_id, self.data) @packet(0x6d04b3da) class WelcomePacket: - def __init__(self, message=''): - self.message = message + def __init__(self, message=''): + self.message = message - def encode(self): - encoded_message = self.message.encode('ascii') - return struct.pack('".format(self.message) + def __str__(self): + return "".format(self.message) @packet(0xe548e74a) class VersionPacket: - def __init__(self, major, minor, patch): - self.major = major - self.minor = minor - self.patch = patch + def __init__(self, major, minor, patch): + self.major = major + self.minor = minor + self.patch = patch - def encode(self): - return pack('IfIII', 0, - float('{}.{}'.format(self.major, self.minor)), - self.major, self.minor, self.patch) + def encode(self): + return pack('IfIII', 0, + float('{}.{}'.format(self.major, self.minor)), + self.major, self.minor, self.patch) - @classmethod - def decode(cls, packet): - unknown_1, legacy_version, major, minor, patch = unpack('IfIII', packet) - return cls(major, minor, patch) + @classmethod + def decode(cls, packet): + unknown_1, legacy_version, major, minor, patch = unpack('IfIII', packet) + return cls(major, minor, patch) - def __str__(self): - return "".format(self.major, self.minor, self.patch) + def __str__(self): + return "".format(self.major, self.minor, self.patch) @packet(0x3de66711) class DifficultyPacket: - def __init__(self, difficulty, game_type): - self.difficulty = difficulty - self.game_type = game_type + def __init__(self, difficulty, game_type): + self.difficulty = difficulty + self.game_type = game_type - def encode(self): - return pack('II', self.difficulty, self.game_type.value) + def encode(self): + return pack('II', self.difficulty, self.game_type.value) - @classmethod - def decode(cls, packet): - difficulty, game_type_raw = unpack('II', packet) - return cls(difficulty, GameType(game_type_raw)) + @classmethod + def decode(cls, packet): + difficulty, game_type_raw = unpack('II', packet) + return cls(difficulty, GameType(game_type_raw)) - def __str__(self): - return "".format(self.difficulty, self.game_type) + def __str__(self): + return "".format(self.difficulty, self.game_type) @packet(0x19c6e2d4) class ConsoleStatusPacket: - def __init__(self, ship, consoles): - self.consoles = {key: consoles.get(key, ConsoleStatus.available) for key in Console} - self.ship = ship - - def encode(self): - return pack('I[B]', self.ship, - [(self.consoles[console].value,) for console in Console]) - - @classmethod - def decode(cls, packet): - ship, body = unpack('I[B]', packet) - body = [x[0] for x in body] - if len(body) != len(Console): - raise ValueError("Incorrect console count ({}, should be {})".format(len(body), len(Console))) - consoles = {console: ConsoleStatus(body[console.value]) for console in Console} - return cls(ship, consoles) - - def __str__(self): - return ''.format(self.ship, - {console: status - for console, status in self.consoles.items() - if status != ConsoleStatus.available}) + def __init__(self, ship, consoles): + self.consoles = {key: consoles.get(key, ConsoleStatus.available) for key in Console} + self.ship = ship + + def encode(self): + return pack('I[B]', self.ship, + [(self.consoles[console].value,) for console in Console]) + + @classmethod + def decode(cls, packet): + ship, body = unpack('I[B]', packet) + body = [x[0] for x in body] + if len(body) != len(Console): + raise ValueError("Incorrect console count ({}, should be {})".format(len(body), len(Console))) + consoles = {console: ConsoleStatus(body[console.value]) for console in Console} + return cls(ship, consoles) + + def __str__(self): + return ''.format(self.ship, + {console: status + for console, status in self.consoles.items() + if status != ConsoleStatus.available}) @packet(0xf5821226) class HeartbeatPacket: - def encode(self): - return b'' + def encode(self): + return b'' - @classmethod - def decode(cls, packet): - if packet != b'': - raise ValueError('Payload in heartbeat') - return cls() + @classmethod + def decode(cls, packet): + if packet != b'': + raise ValueError('Payload in heartbeat') + return cls() - def __str__(self): - return "" + def __str__(self): + return "" @packet(0xee665279) class IntelPacket: - def __init__(self, object, intel): - self.object = object - self.intel = intel + def __init__(self, object, intel): + self.object = object + self.intel = intel - def encode(self): - return pack('Ibu', self.object, 3, self.intel) + def encode(self): + return pack('Ibu', self.object, 3, self.intel) - @classmethod - def decode(cls, packet): - object, _unk, intel = unpack('Ibu', packet) - return cls(object, intel) + @classmethod + def decode(cls, packet): + object, _unk, intel = unpack('Ibu', packet) + return cls(object, intel) - def __str__(self): - return ''.format(self.object, self.intel) + def __str__(self): + return ''.format(self.object, self.intel) @packet(0xd672c35f) class CommsIncomingPacket: - def __init__(self, priority, sender, message): - self.priority = priority - self.sender = sender - self.message = message + def __init__(self, priority, sender, message): + self.priority = priority + self.sender = sender + self.message = message - def encode(self): - return pack('Suu', self.priority, self.sender, self.message.replace('\n', '^')) + def encode(self): + return pack('Suu', self.priority, self.sender, self.message.replace('\n', '^')) - @classmethod - def decode(cls, packet): - prio, sender, message = unpack('Suu', packet) - return cls(prio, sender, message.replace('^', '\n')) + @classmethod + def decode(cls, packet): + prio, sender, message = unpack('Suu', packet) + return cls(prio, sender, message.replace('^', '\n')) - def __str__(self): - return ''.format(self.priority, self.sender, self.message) + def __str__(self): + return ''.format(self.priority, self.sender, self.message) @packet(0x80803df9) class ObjectUpdatePacket: - def __init__(self, raw_data): - self.raw_data = raw_data - - @property - def _records(self): - return decode_obj_update_packet(self.raw_data) - - @property - def records(self): - try: - return self._records - except Exception: - return [] - - @classmethod - def decode(cls, packet): - if packet == b'\x00\x00\x00\x00': - return NoisePacket() - return cls(packet) - - def encode(self): - return self.raw_data - - def __str__(self): - try: - records = repr(self._records) - return ''.format(records) - except Exception as e: - return ''.format(self.raw_data, e) + def __init__(self, raw_data): + self.raw_data = raw_data + + @property + def _records(self): + return decode_obj_update_packet(self.raw_data) + + @property + def records(self): + try: + return self._records + except Exception: + return [] + + @classmethod + def decode(cls, packet): + if packet == b'\x00\x00\x00\x00': + return NoisePacket() + return cls(packet) + + def encode(self): + return self.raw_data + + def __str__(self): + try: + records = repr(self._records) + return ''.format(records) + except Exception as e: + return ''.format(self.raw_data, e) class NoisePacket: - def __init__(self): - self.packet_id = 0x80803df9 + def __init__(self): + self.packet_id = 0x80803df9 - def encode(self): - return b'\x00\x00\x00\x00' + def encode(self): + return b'\x00\x00\x00\x00' - def __str__(self): - return '' + def __str__(self): + return '' @packet(0xcc5a3e30) class DestroyObjectPacket: - def __init__(self, type, object): - self.type = type - self.object = object + def __init__(self, type, object): + self.type = type + self.object = object - def encode(self): - return pack('BI', self.type.value, self.object) + def encode(self): + return pack('BI', self.type.value, self.object) - @classmethod - def decode(cls, packet): - type, object = unpack('BI', packet) - return cls(type=ObjectType(type), object=object) + @classmethod + def decode(cls, packet): + type, object = unpack('BI', packet) + return cls(type=ObjectType(type), object=object) - def __str__(self): - return ''.format(self.type, self.object) + def __str__(self): + return ''.format(self.type, self.object) @packet(0xf754c8fe) class GameMessagePacket: - @classmethod - def decode(cls, packet): - if not packet: - raise ValueError('No payload in game message') - subtype_index = packet[0] - if subtype_index == 0: - return GameStartPacket.decode(packet) - if subtype_index == 6: - return GameEndPacket.decode(packet) - if subtype_index == 9: - return SkyboxPacket.decode(packet) - if subtype_index == 10: - return PopupPacket.decode(packet) - if subtype_index == 11: - return AutonomousDamconPacket.decode(packet) - if subtype_index == 12: - return JumpStartPacket.decode(packet) - if subtype_index == 13: - return JumpEndPacket.decode(packet) - if subtype_index == 15: - return AllShipSettingsPacket.decode(packet) - if subtype_index == 16: - return DmxPacket.decode(packet) - raise SoftDecodeFailure() + @classmethod + def decode(cls, packet): + if not packet: + raise ValueError('No payload in game message') + subtype_index = packet[0] + if subtype_index == 0: + return GameStartPacket.decode(packet) + if subtype_index == 6: + return GameEndPacket.decode(packet) + if subtype_index == 9: + return SkyboxPacket.decode(packet) + if subtype_index == 10: + return PopupPacket.decode(packet) + if subtype_index == 11: + return AutonomousDamconPacket.decode(packet) + if subtype_index == 12: + return JumpStartPacket.decode(packet) + if subtype_index == 13: + return JumpEndPacket.decode(packet) + if subtype_index == 15: + return AllShipSettingsPacket.decode(packet) + if subtype_index == 16: + return DmxPacket.decode(packet) + raise SoftDecodeFailure() class GameStartPacket(GameMessagePacket): - def encode(self): - return b'\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00' + def encode(self): + return b'\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00' - @classmethod - def decode(cls, packet): - if len(packet) != 12: - raise ValueError('Wrong packet length') - return cls() + @classmethod + def decode(cls, packet): + if len(packet) != 12: + raise ValueError('Wrong packet length') + return cls() - def __str__(self): - return '' + def __str__(self): + return '' class GameEndPacket(GameMessagePacket): - def encode(self): - return b'\x06\x00\x00\x00' + def encode(self): + return b'\x06\x00\x00\x00' - @classmethod - def decode(cls, packet): - if len(packet) != 4: - raise ValueError('Wrong packet length') - return cls() + @classmethod + def decode(cls, packet): + if len(packet) != 4: + raise ValueError('Wrong packet length') + return cls() - def __str__(self): - return '' + def __str__(self): + return '' class AllShipSettingsPacket(GameMessagePacket): - def __init__(self, ships): - self.ships = list(ships) - if len(self.ships) != 8: - raise ValueError('Must be 8 ships, {} given'.format(len(self.ships))) + def __init__(self, ships): + self.ships = list(ships) + if len(self.ships) != 8: + raise ValueError('Must be 8 ships, {} given'.format(len(self.ships))) - def encode(self): - return pack('I[IIIu]', 15, - [(ship.drive.value, ship.type.value, 1, ship.name) - for ship in self.ships]) + def encode(self): + return pack('I[IIIu]', 15, + [(ship.drive.value, ship.type.value, 1, ship.name) + for ship in self.ships]) - @classmethod - def decode(cls, packet): - _id, records = unpack('I[IIfIu]', packet) - return cls(ShipSettingsRecord(DriveType(drv), ShipType(typ), name) - for drv, typ, _accent, _what, name in records) + @classmethod + def decode(cls, packet): + _id, records = unpack('I[IIfIu]', packet) + return cls(ShipSettingsRecord(DriveType(drv), ShipType(typ), name) + for drv, typ, _accent, _what, name in records) - def __str__(self): - return ''.format(self.ships) + def __str__(self): + return ''.format(self.ships) class JumpStartPacket(GameMessagePacket): - def encode(self): - return b'\x0c\x00\x00\x00' + def encode(self): + return b'\x0c\x00\x00\x00' - @classmethod - def decode(cls, packet): - if len(packet) != 4: - raise ValueError('Wrong packet length') - return cls() + @classmethod + def decode(cls, packet): + if len(packet) != 4: + raise ValueError('Wrong packet length') + return cls() - def __str__(self): - return '' + def __str__(self): + return '' class JumpEndPacket(GameMessagePacket): - def encode(self): - return b'\x0d\x00\x00\x00' + def encode(self): + return b'\x0d\x00\x00\x00' - @classmethod - def decode(cls, packet): - if len(packet) != 4: - raise ValueError('Wrong packet length') - return cls() + @classmethod + def decode(cls, packet): + if len(packet) != 4: + raise ValueError('Wrong packet length') + return cls() - def __str__(self): - return '' + def __str__(self): + return '' class DmxPacket(GameMessagePacket): - def __init__(self, flag, state): - self.flag = flag - self.state = state + def __init__(self, flag, state): + self.flag = flag + self.state = state - def encode(self): - return pack('IuI', 0x10, self.flag, int(self.state)) + def encode(self): + return pack('IuI', 0x10, self.flag, int(self.state)) - @classmethod - def decode(cls, packet): - _id, flag, state = unpack('IuI', packet) - return cls(flag, state) + @classmethod + def decode(cls, packet): + _id, flag, state = unpack('IuI', packet) + return cls(flag, state) - def __str__(self): - return ''.format(self.flag, self.state) + def __str__(self): + return ''.format(self.flag, self.state) class SkyboxPacket(GameMessagePacket): - def __init__(self, skybox): - self.skybox = skybox + def __init__(self, skybox): + self.skybox = skybox - def encode(self): - return pack('II', 9, self.skybox) + def encode(self): + return pack('II', 9, self.skybox) - @classmethod - def decode(cls, packet): - _id, skybox = unpack('II', packet) - return cls(skybox) + @classmethod + def decode(cls, packet): + _id, skybox = unpack('II', packet) + return cls(skybox) - def __str__(self): - return ''.format(self.skybox) + def __str__(self): + return ''.format(self.skybox) class PopupPacket(GameMessagePacket): - def __init__(self, message): - self.message = message + def __init__(self, message): + self.message = message - def encode(self): - return pack('Iu', 0x0a, self.message) + def encode(self): + return pack('Iu', 0x0a, self.message) - @classmethod - def decode(cls, packet): - _id, message = unpack('Iu', packet) - return cls(message) + @classmethod + def decode(cls, packet): + _id, message = unpack('Iu', packet) + return cls(message) - def __str__(self): - return ''.format(self.message) + def __str__(self): + return ''.format(self.message) class AutonomousDamconPacket(GameMessagePacket): - def __init__(self, autonomy): - self.autonomy = autonomy + def __init__(self, autonomy): + self.autonomy = autonomy - def encode(self): - return pack('II', 0x0b, int(self.autonomy)) + def encode(self): + return pack('II', 0x0b, int(self.autonomy)) - @classmethod - def decode(cls, packet): - _id, autonomy = unpack('II', packet) - return cls(bool(autonomy)) + @classmethod + def decode(cls, packet): + _id, autonomy = unpack('II', packet) + return cls(bool(autonomy)) - def __str__(self): - return ''.format(self.autonomy) + def __str__(self): + return ''.format(self.autonomy) @packet(0x4c821d3c) class ShipAction1Packet: - @classmethod - def decode(cls, packet): - if not packet: - raise ValueError('No payload in game message') - subtype_index = packet[0] - if subtype_index == 0: - return HelmSetWarpPacket.decode(packet) - if subtype_index == 1: - return SetMainScreenPacket.decode(packet) - if subtype_index == 2: - return SetWeaponsTargetPacket.decode(packet) - if subtype_index == 3: - return ToggleAutoBeamsPacket.decode(packet) - if subtype_index == 4: - return ToggleShieldsPacket.decode(packet) - if subtype_index == 7: - return HelmRequestDockPacket.decode(packet) - if subtype_index == 10: - return ToggleRedAlertPacket.decode(packet) - if subtype_index == 11: - return SetBeamFreqPacket.decode(packet) - if subtype_index == 13: - return SetShipPacket.decode(packet) - if subtype_index == 14: - return SetConsolePacket.decode(packet) - if subtype_index == 15: - return ReadyPacket.decode(packet) - if subtype_index == 16: - return SciSelectPacket.decode(packet) - if subtype_index == 17: - return CaptainSelectPacket.decode(packet) - if subtype_index == 18: - return GameMasterSelectPacket.decode(packet) - if subtype_index == 19: - return SciScanPacket.decode(packet) - if subtype_index == 22: - return SetShipSettingsPacket.decode(packet) - if subtype_index == 24: - return HelmToggleReversePacket.decode(packet) - if subtype_index == 25: - return Ready2Packet.decode(packet) - if subtype_index == 26: - return TogglePerspectivePacket.decode(packet) - if subtype_index == 27: - return ClimbDivePacket.decode(packet) - raise SoftDecodeFailure() + @classmethod + def decode(cls, packet): + if not packet: + raise ValueError('No payload in game message') + subtype_index = packet[0] + if subtype_index == 0: + return HelmSetWarpPacket.decode(packet) + if subtype_index == 1: + return SetMainScreenPacket.decode(packet) + if subtype_index == 2: + return SetWeaponsTargetPacket.decode(packet) + if subtype_index == 3: + return ToggleAutoBeamsPacket.decode(packet) + if subtype_index == 4: + return ToggleShieldsPacket.decode(packet) + if subtype_index == 7: + return HelmRequestDockPacket.decode(packet) + if subtype_index == 10: + return ToggleRedAlertPacket.decode(packet) + if subtype_index == 11: + return SetBeamFreqPacket.decode(packet) + if subtype_index == 13: + return SetShipPacket.decode(packet) + if subtype_index == 14: + return SetConsolePacket.decode(packet) + if subtype_index == 15: + return ReadyPacket.decode(packet) + if subtype_index == 16: + return SciSelectPacket.decode(packet) + if subtype_index == 17: + return CaptainSelectPacket.decode(packet) + if subtype_index == 18: + return GameMasterSelectPacket.decode(packet) + if subtype_index == 19: + return SciScanPacket.decode(packet) + if subtype_index == 22: + return SetShipSettingsPacket.decode(packet) + if subtype_index == 24: + return HelmToggleReversePacket.decode(packet) + if subtype_index == 25: + return Ready2Packet.decode(packet) + if subtype_index == 26: + return TogglePerspectivePacket.decode(packet) + if subtype_index == 27: + return ClimbDivePacket.decode(packet) + raise SoftDecodeFailure() class SciScanPacket(ShipAction1Packet): - def __init__(self, target): - self.target = target + def __init__(self, target): + self.target = target - def encode(self): - return pack('II', 19, self.target) + def encode(self): + return pack('II', 19, self.target) - @classmethod - def decode(cls, packet): - _idx, tgt = unpack('II', packet) - return cls(tgt) + @classmethod + def decode(cls, packet): + _idx, tgt = unpack('II', packet) + return cls(tgt) - def __str__(self): - return "".format(self.target) + def __str__(self): + return "".format(self.target) class CaptainSelectPacket(ShipAction1Packet): - def __init__(self, object): - self.object = object - - def encode(self): - if self.object is not None: - return pack('II', 17, self.object) - else: - return pack('II', 17, 1) - - @classmethod - def decode(cls, packet): - _idx, tgt = unpack('II', packet) - if tgt != 1: - return cls(tgt) - else: - return cls(None) - - def __str__(self): - return "".format(self.object) + def __init__(self, object): + self.object = object + + def encode(self): + if self.object is not None: + return pack('II', 17, self.object) + else: + return pack('II', 17, 1) + + @classmethod + def decode(cls, packet): + _idx, tgt = unpack('II', packet) + if tgt != 1: + return cls(tgt) + else: + return cls(None) + + def __str__(self): + return "".format(self.object) class GameMasterSelectPacket(ShipAction1Packet): - def __init__(self, object): - self.object = object - - def encode(self): - if self.object is not None: - return pack('II', 18, self.object) - else: - return pack('II', 18, 1) - - @classmethod - def decode(cls, packet): - _idx, tgt = unpack('II', packet) - if tgt != 1: - return cls(tgt) - else: - return cls(None) - - def __str__(self): - return "".format(self.object) + def __init__(self, object): + self.object = object + + def encode(self): + if self.object is not None: + return pack('II', 18, self.object) + else: + return pack('II', 18, 1) + + @classmethod + def decode(cls, packet): + _idx, tgt = unpack('II', packet) + if tgt != 1: + return cls(tgt) + else: + return cls(None) + + def __str__(self): + return "".format(self.object) class SciSelectPacket(ShipAction1Packet): - def __init__(self, object): - self.object = object - - def encode(self): - if self.object is not None: - return pack('II', 16, self.object) - else: - return pack('II', 16, 1) - - @classmethod - def decode(cls, packet): - _idx, tgt = unpack('II', packet) - if tgt != 1: - return cls(tgt) - else: - return cls(None) - - def __str__(self): - return "".format(self.object) + def __init__(self, object): + self.object = object + + def encode(self): + if self.object is not None: + return pack('II', 16, self.object) + else: + return pack('II', 16, 1) + + @classmethod + def decode(cls, packet): + _idx, tgt = unpack('II', packet) + if tgt != 1: + return cls(tgt) + else: + return cls(None) + + def __str__(self): + return "".format(self.object) class SetWeaponsTargetPacket(ShipAction1Packet): - def __init__(self, object): - self.object = object - - def encode(self): - if self.object is not None: - return pack('II', 2, self.object) - else: - return pack('II', 2, 1) - - @classmethod - def decode(cls, packet): - _idx, tgt = unpack('II', packet) - if tgt != 1: - return cls(tgt) - else: - return cls(None) - - def __str__(self): - return "".format(self.object) + def __init__(self, object): + self.object = object + + def encode(self): + if self.object is not None: + return pack('II', 2, self.object) + else: + return pack('II', 2, 1) + + @classmethod + def decode(cls, packet): + _idx, tgt = unpack('II', packet) + if tgt != 1: + return cls(tgt) + else: + return cls(None) + + def __str__(self): + return "".format(self.object) class SetBeamFreqPacket(ShipAction1Packet): - def __init__(self, freq): - self.freq = freq + def __init__(self, freq): + self.freq = freq - def encode(self): - return pack('II', 11, self.freq) + def encode(self): + return pack('II', 11, self.freq) - @classmethod - def decode(cls, packet): - _idx, freq = unpack('II', packet) - return cls(freq) + @classmethod + def decode(cls, packet): + _idx, freq = unpack('II', packet) + return cls(freq) - def __str__(self): - return "".format(self.freq) + def __str__(self): + return "".format(self.freq) class HelmToggleReversePacket(ShipAction1Packet): - def encode(self): - return b'\x18\x00\x00\x00\x00\x00\x00\x00' + def encode(self): + return b'\x18\x00\x00\x00\x00\x00\x00\x00' - @classmethod - def decode(cls, packet): - if packet != b'\x18\x00\x00\x00\x00\x00\x00\x00': - raise ValueError('Unexpected payload in reverse packet') - return cls() + @classmethod + def decode(cls, packet): + if packet != b'\x18\x00\x00\x00\x00\x00\x00\x00': + raise ValueError('Unexpected payload in reverse packet') + return cls() - def __str__(self): - return '' + def __str__(self): + return '' class ReadyPacket(ShipAction1Packet): - def encode(self): - return b'\x0f\x00\x00\x00\x00\x00\x00\x00' + def encode(self): + return b'\x0f\x00\x00\x00\x00\x00\x00\x00' - @classmethod - def decode(cls, packet): - if packet != b'\x0f\x00\x00\x00\x00\x00\x00\x00': - raise ValueError('Unexpected payload in ready packet') - return cls() + @classmethod + def decode(cls, packet): + if packet != b'\x0f\x00\x00\x00\x00\x00\x00\x00': + raise ValueError('Unexpected payload in ready packet') + return cls() - def __str__(self): - return '' + def __str__(self): + return '' class Ready2Packet(ShipAction1Packet): - def encode(self): - return b'\x19\x00\x00\x00\x00\x00\x00\x00' + def encode(self): + return b'\x19\x00\x00\x00\x00\x00\x00\x00' - @classmethod - def decode(cls, packet): - if packet != b'\x19\x00\x00\x00\x00\x00\x00\x00': - raise ValueError('Unexpected payload in ready2 packet') - return cls() + @classmethod + def decode(cls, packet): + if packet != b'\x19\x00\x00\x00\x00\x00\x00\x00': + raise ValueError('Unexpected payload in ready2 packet') + return cls() - def __str__(self): - return '' + def __str__(self): + return '' class SetShipSettingsPacket(ShipAction1Packet): - def __init__(self, drive, type, name): - self.drive = drive - self.type = type - self.name = name + def __init__(self, drive, type, name): + self.drive = drive + self.type = type + self.name = name - def encode(self): - return pack('IIIIu', 0x16, self.drive.value, self.type.value, 1, self.name) + def encode(self): + return pack('IIIIu', 0x16, self.drive.value, self.type.value, 1, self.name) - @classmethod - def decode(cls, packet): - _id, drv, typ, _unk, name = unpack('IIIIu', packet) - return cls(drive=DriveType(drv), - type=ShipType(typ), - name=name) + @classmethod + def decode(cls, packet): + _id, drv, typ, _unk, name = unpack('IIIIu', packet) + return cls(drive=DriveType(drv), + type=ShipType(typ), + name=name) - def __str__(self): - return ''.format(self.drive, self.type, self.name) + def __str__(self): + return ''.format(self.drive, self.type, self.name) class HelmRequestDockPacket(ShipAction1Packet): - def encode(self): - return b'\x07\x00\x00\x00\x00\x00\x00\x00' + def encode(self): + return b'\x07\x00\x00\x00\x00\x00\x00\x00' - @classmethod - def decode(cls, data): - if data != b'\x07\x00\x00\x00\x00\x00\x00\x00': - raise SoftDecodeFailure() - return cls() + @classmethod + def decode(cls, data): + if data != b'\x07\x00\x00\x00\x00\x00\x00\x00': + raise SoftDecodeFailure() + return cls() - def __str__(self): - return '' + def __str__(self): + return '' class ToggleShieldsPacket(ShipAction1Packet): - def encode(self): - return b'\x04\x00\x00\x00\x00\x00\x00\x00' + def encode(self): + return b'\x04\x00\x00\x00\x00\x00\x00\x00' - @classmethod - def decode(cls, data): - if data != b'\x04\x00\x00\x00\x00\x00\x00\x00': - raise SoftDecodeFailure() - return cls() + @classmethod + def decode(cls, data): + if data != b'\x04\x00\x00\x00\x00\x00\x00\x00': + raise SoftDecodeFailure() + return cls() - def __str__(self): - return '' + def __str__(self): + return '' class ToggleRedAlertPacket(ShipAction1Packet): - def encode(self): - return b'\x0a\x00\x00\x00\x00\x00\x00\x00' + def encode(self): + return b'\x0a\x00\x00\x00\x00\x00\x00\x00' - @classmethod - def decode(cls, data): - if data != b'\x0a\x00\x00\x00\x00\x00\x00\x00': - raise SoftDecodeFailure() - return cls() + @classmethod + def decode(cls, data): + if data != b'\x0a\x00\x00\x00\x00\x00\x00\x00': + raise SoftDecodeFailure() + return cls() - def __str__(self): - return '' + def __str__(self): + return '' class ToggleAutoBeamsPacket(ShipAction1Packet): - def encode(self): - return b'\x03\x00\x00\x00\x00\x00\x00\x00' + def encode(self): + return b'\x03\x00\x00\x00\x00\x00\x00\x00' - @classmethod - def decode(cls, data): - if data != b'\x03\x00\x00\x00\x00\x00\x00\x00': - raise SoftDecodeFailure() - return cls() + @classmethod + def decode(cls, data): + if data != b'\x03\x00\x00\x00\x00\x00\x00\x00': + raise SoftDecodeFailure() + return cls() - def __str__(self): - return '' + def __str__(self): + return '' class TogglePerspectivePacket(ShipAction1Packet): - def encode(self): - return b'\x1a\x00\x00\x00\x00\x00\x00\x00' + def encode(self): + return b'\x1a\x00\x00\x00\x00\x00\x00\x00' - @classmethod - def decode(cls, data): - if data != b'\x1a\x00\x00\x00\x00\x00\x00\x00': - raise SoftDecodeFailure() - return cls() + @classmethod + def decode(cls, data): + if data != b'\x1a\x00\x00\x00\x00\x00\x00\x00': + raise SoftDecodeFailure() + return cls() - def __str__(self): - return '' + def __str__(self): + return '' class ClimbDivePacket(ShipAction1Packet): - def __init__(self, direction): - self.direction = direction + def __init__(self, direction): + self.direction = direction - def encode(self): - return pack('Ii', 27, self.direction) + def encode(self): + return pack('Ii', 27, self.direction) - @classmethod - def decode(cls, packet): - _id, direction = unpack('Ii', packet) - return cls(direction) + @classmethod + def decode(cls, packet): + _id, direction = unpack('Ii', packet) + return cls(direction) - def __str__(self): - return "".format(self.direction) + def __str__(self): + return "".format(self.direction) class SetMainScreenPacket(ShipAction1Packet): - def __init__(self, screen): - self.screen = screen + def __init__(self, screen): + self.screen = screen - def encode(self): - return pack('II', 1, self.screen.value) + def encode(self): + return pack('II', 1, self.screen.value) - @classmethod - def decode(cls, packet): - _idx, screen_id = unpack('II', packet) - return cls(MainView(screen_id)) + @classmethod + def decode(cls, packet): + _idx, screen_id = unpack('II', packet) + return cls(MainView(screen_id)) - def __str__(self): - return "".format(self.screen) + def __str__(self): + return "".format(self.screen) class SetConsolePacket(ShipAction1Packet): - def __init__(self, console, selected): - self.console = console - self.selected = selected + def __init__(self, console, selected): + self.console = console + self.selected = selected - def encode(self): - return pack('III', 0x0e, self.console.value, 1 if self.selected else 0) + def encode(self): + return pack('III', 0x0e, self.console.value, 1 if self.selected else 0) - @classmethod - def decode(cls, packet): - _idx, console_id, selected = unpack('III', packet) - return cls(Console(console_id), bool(selected)) + @classmethod + def decode(cls, packet): + _idx, console_id, selected = unpack('III', packet) + return cls(Console(console_id), bool(selected)) - def __str__(self): - return "".format(self.console, self.selected) + def __str__(self): + return "".format(self.console, self.selected) class HelmSetWarpPacket(ShipAction1Packet): - def __init__(self, warp): - self.warp = warp + def __init__(self, warp): + self.warp = warp - def encode(self): - return pack('II', 0, self.warp) + def encode(self): + return pack('II', 0, self.warp) - @classmethod - def decode(cls, packet): - _idx, warp = unpack('II', packet) - return cls(warp) + @classmethod + def decode(cls, packet): + _idx, warp = unpack('II', packet) + return cls(warp) - def __str__(self): - return "".format(self.warp) + def __str__(self): + return "".format(self.warp) class SetShipPacket(ShipAction1Packet): - def __init__(self, ship): - self.ship = ship + def __init__(self, ship): + self.ship = ship - def encode(self): - return pack('II', 0x0d, self.ship) + def encode(self): + return pack('II', 0x0d, self.ship) - @classmethod - def decode(cls, packet): - _idx, ship = unpack('II', packet) - return cls(ship) + @classmethod + def decode(cls, packet): + _idx, ship = unpack('II', packet) + return cls(ship) - def __str__(self): - return "".format(self.ship) + def __str__(self): + return "".format(self.ship) @packet(0x0351a5ac) class ShipAction3Packet: - @classmethod - def decode(cls, packet): - if not packet: - raise ValueError('No payload in game message') - subtype_index = packet[0] - if subtype_index == 0: - return HelmSetImpulsePacket.decode(packet) - if subtype_index == 1: - return HelmSetSteeringPacket.decode(packet) - if subtype_index == 5: - return HelmJumpPacket.decode(packet) - raise SoftDecodeFailure() + @classmethod + def decode(cls, packet): + if not packet: + raise ValueError('No payload in game message') + subtype_index = packet[0] + if subtype_index == 0: + return HelmSetImpulsePacket.decode(packet) + if subtype_index == 1: + return HelmSetSteeringPacket.decode(packet) + if subtype_index == 5: + return HelmJumpPacket.decode(packet) + raise SoftDecodeFailure() class HelmSetSteeringPacket(ShipAction3Packet): - def __init__(self, rudder): - self.rudder = rudder + def __init__(self, rudder): + self.rudder = rudder - def encode(self): - return pack('If', 1, self.rudder) + def encode(self): + return pack('If', 1, self.rudder) - @classmethod - def decode(cls, packet): - _idx, rudder = unpack('If', packet) - return cls(rudder) + @classmethod + def decode(cls, packet): + _idx, rudder = unpack('If', packet) + return cls(rudder) - def __str__(self): - return ''.format(self.rudder) + def __str__(self): + return ''.format(self.rudder) class HelmSetImpulsePacket(ShipAction3Packet): - def __init__(self, impulse): - self.impulse = impulse + def __init__(self, impulse): + self.impulse = impulse - def encode(self): - return pack('If', 0, self.impulse) + def encode(self): + return pack('If', 0, self.impulse) - @classmethod - def decode(cls, packet): - _idx, impulse = unpack('If', packet) - return cls(impulse) + @classmethod + def decode(cls, packet): + _idx, impulse = unpack('If', packet) + return cls(impulse) - def __str__(self): - return ''.format(self.impulse) + def __str__(self): + return ''.format(self.impulse) class HelmJumpPacket(ShipAction3Packet): - def __init__(self, bearing, distance): - self.bearing = bearing - self.distance = distance + def __init__(self, bearing, distance): + self.bearing = bearing + self.distance = distance - def encode(self): - return pack('Iff', 5, self.bearing / (math.pi * 2), self.distance / 50) + def encode(self): + return pack('Iff', 5, self.bearing / (math.pi * 2), self.distance / 50) - @classmethod - def decode(cls, packet): - _idx, bearing, distance = unpack('Iff', packet) - return cls(bearing * (math.pi * 2), distance * 50) + @classmethod + def decode(cls, packet): + _idx, bearing, distance = unpack('Iff', packet) + return cls(bearing * (math.pi * 2), distance * 50) - def __str__(self): - return ''.format(self.bearing, self.distance) + def __str__(self): + return ''.format(self.bearing, self.distance) @packet(0xb83fd2c4) class BeamFiredPacket: - def __init__(self, object, port, origin, target, x, y, z, auto): - self.object = object - self.port = port - self.origin = origin - self.target = target - self.x = x - self.y = y - self.z = z - self.auto = auto - - def encode(self): - return pack('IIIIIIIIIfffI', - self.object, 1, 1200, - self.port, - 1, 1, 0, - self.origin, self.target, - self.x, self.y, self.z, - 0 if self.auto else 1) - - @classmethod - def decode(cls, packet): - object, _unk1, _unk2, port, origintype, targettype, _unk3, origin, target, x, y, z, auto = unpack('IIIIIIIIIfffI', packet) - return cls(object, port, origin, target, x, y, z, [True, False][auto]) - - def __str__(self): - return ''.format(**self.__dict__) + def __init__(self, object, port, origin, target, x, y, z, auto): + self.object = object + self.port = port + self.origin = origin + self.target = target + self.x = x + self.y = y + self.z = z + self.auto = auto + + def encode(self): + return pack('IIIIIIIIIfffI', + self.object, 1, 1200, + self.port, + 1, 1, 0, + self.origin, self.target, + self.x, self.y, self.z, + 0 if self.auto else 1) + + @classmethod + def decode(cls, packet): + object, _unk1, _unk2, port, origintype, targettype, _unk3, origin, target, x, y, z, auto = unpack('IIIIIIIIIfffI', packet) + return cls(object, port, origin, target, x, y, z, [True, False][auto]) + + def __str__(self): + return ''.format(**self.__dict__) def encode(packet, provenance=PacketProvenance.client): - encoded_block = packet.encode() - block_len = len(encoded_block) - return (struct.pack(' 0: - sys.stderr.write("WARNING: skipping {} bytes of stream to resync\n".format(de_index)) - sys.stderr.flush() - packet = packet[de_index:] - elif de_index == -1: - # wtf? - return [], b'' - buffer_len = len(packet) - if buffer_len < 24: - return [], packet - header, packet_len, origin, padding, remaining, ptype = struct.unpack(' 0: + sys.stderr.write("WARNING: skipping {} bytes of stream to resync\n".format(de_index)) + sys.stderr.flush() + packet = packet[de_index:] + elif de_index == -1: + # wtf? + return [], b'' + buffer_len = len(packet) + if buffer_len < 24: + return [], packet + header, packet_len, origin, padding, remaining, ptype = struct.unpack(' Date: Sun, 18 Mar 2018 23:44:03 -0500 Subject: [PATCH 6/9] updated example.py better scienceAI, but scans same ship repeatedly still..... and does not differentiate friend from foe --- example.py | 94 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 83 insertions(+), 11 deletions(-) diff --git a/example.py b/example.py index 5a128e7..7a01490 100755 --- a/example.py +++ b/example.py @@ -6,11 +6,12 @@ pp = pprint.PrettyPrinter(indent=2) # 172.16.104.171 -tx, rx = diana.connect('172.89.225.88') # or whatever IP, this is my local server -tx(diana.packet.SetShipPacket(1)) # Select Ship 1 +tx, rx = diana.connect('127.0.0.1') # or whatever IP +tx(diana.packet.SetShipPacket(1)) # Select Ship by #, Artemis = 1, etc.. for console in diana.packet.Console: # select a bunch of consoles if console.value not in (3,4,5): continue + # 3,4,5 = eng, sci, comms tx(diana.packet.SetConsolePacket(console,True)) # 1-5: helm,weapons,engineering,science,comms @@ -18,14 +19,85 @@ tracker = diana.tracking.Tracker() class ScienceAI: - def update_scans(): - pass - def reset(): - pass + def __init__(self,tx,rx,tracker): + self._tx = tx + self._rx = rx + self._tracker = tracker + + self.scannable = [5,8,15] # other_ship, anomaly, creature + self.known_objects = {} + + def try_scan(self,obj_id): + self._tx( + diana.packet.SciScanPacket(obj_id) + ) + + def is_scanning(self): + try: return (self._tracker.player_ship['scanning-id'] not in (0,None)) + except KeyError: + return False + + def tracker_callback(self,oid): + try: + if self._tracker.player_ship['object'] == oid: + self.update() # update if player_ship updated, else give up + except KeyError: # if we cant find player_ship yet, just ignore + pass + + def update(self): + # update list of known objects + for _,obj in self._tracker.objects.items(): + try: + assert type(obj['type']) is diana.enumerations.ObjectType + except KeyError: + raise Exception("Bad obj['type'] on object \""+repr(obj)+"\"") + + if obj['type'].value in self.scannable: + obj_id = obj['object'] + if obj_id not in self.known_objects.keys(): + self.known_objects[obj_id] = { + "scan-level":0, # not seen =?=> not scanned? + #"distance":None # calculate on demand + } + else: + try: + self.known_objects[obj_id]['scan-level'] = self._tracker.objects[obj_id]['scan-level?'] + except: + pass + + # print list of known objects + if len(self.known_objects.keys()) > 0: + print("ScienceAI update:") + #print("\t%d objects known"%len(self.known_objects.keys())) + print("\t ObjID\t Info") + for k,v in self.known_objects.items(): + print("\t",k,"\t",v) + print("\tSCANNING_STATE:\t",self.is_scanning()) + if(self.is_scanning()): + print("\tSCANNING_PROGRESS:\t", + "%d"%(100*(self._tracker.player_ship['scanning-progress']),) + "%" + ) + else: + return # nothing to do anyway + + # if not scanning, scan! + if not self.is_scanning(): + unscanned = [oid for oid in self.known_objects.keys() + if self.known_objects[oid]['scan-level'] < 1] + if not len(unscanned): # no more to scan left = just stop + return + self.try_scan(unscanned[0]) + + +if __name__ == "__main__": + SciAI = ScienceAI(tx,rx,tracker) # instantiate science AI instance + tracker.bind_to_updates(SciAI.tracker_callback) # call this func on obj updates + while True: + for packet in rx: # stream packets from the server + tracker.rx(packet) # Update the tracker with new information + #pp.pprint(tracker.objects) + #for k,v in tracker.player_ship.items(): + # if k in ["science-target","scanning-id","scanning-progress"]: + # print(k,v) -while True: - for packet in rx: # stream packets from the server - tracker.rx(packet) # Update the tracker with new information - #pp.pprint(tracker.objects) - #pp.pprint(tracker.player_ship) From ff3143feaed2f96d2e026b52f69149b083acee38 Mon Sep 17 00:00:00 2001 From: Nicholas Hammond Date: Mon, 19 Mar 2018 19:06:04 -0500 Subject: [PATCH 7/9] spelling and alignment --- diana/object_update.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/diana/object_update.py b/diana/object_update.py index ac7642a..4709606 100644 --- a/diana/object_update.py +++ b/diana/object_update.py @@ -123,18 +123,18 @@ def unscramble_elites(field): "1.3" : ('z','f'), "1.4" : ('red','f'), "1.5" : ('green','f'), - "1.6" : ('blau','f'), + "1.6" : ('blue','f'), "1.7" : ('unused?-1','I'), "1.8" : ('unused?-2','I') }, ObjectType.other_ship : { # NPC ship "1.1" : ('name', 'u'), # im too indecisive to pick whether "1.2" : ('throttle', 'f'), # i want any tab-alignment on this - "1.3" : ('rudder', 'f'), # at all, or where when i do - "1.4" : ('max-impulse', 'f'), + "1.3" : ('rudder', 'f'), # at all, or where when i do + "1.4" : ('max-impulse', 'f'), "1.5" : ('max-turn-rate', 'f'), # NONE OF IT LOOKS RIGHT - "1.6" : ('enemy?', 'I'), - "1.7" : ('vessel-type', 'I'), + "1.6" : ('enemy?', 'I'), + "1.7" : ('vessel-type', 'I'), "1.8" : ('x', 'f'), "2.1" : ('y', 'f'), # i dont know why im even working on this library From 9f9c1e5758658a9829582bc98dd813da493dbe60 Mon Sep 17 00:00:00 2001 From: Nicholas Hammond Date: Mon, 19 Mar 2018 19:06:14 -0500 Subject: [PATCH 8/9] Playing with Plotting --- example.py | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/example.py b/example.py index 7a01490..69e17b1 100755 --- a/example.py +++ b/example.py @@ -67,14 +67,14 @@ def update(self): # print list of known objects if len(self.known_objects.keys()) > 0: - print("ScienceAI update:") + #print("ScienceAI update:") #print("\t%d objects known"%len(self.known_objects.keys())) - print("\t ObjID\t Info") - for k,v in self.known_objects.items(): - print("\t",k,"\t",v) + #print("\t ObjID\t Info") + #for k,v in self.known_objects.items(): + # print("\t",k,"\t",v) print("\tSCANNING_STATE:\t",self.is_scanning()) if(self.is_scanning()): - print("\tSCANNING_PROGRESS:\t", + print("\tSCAN_PROGRESS:\t", "%d"%(100*(self._tracker.player_ship['scanning-progress']),) + "%" ) else: @@ -90,12 +90,38 @@ def update(self): if __name__ == "__main__": + + import tkinter as tk + + tk_root = tk.Tk() + tk_w = tk.Canvas(tk_root, width=800, height=600) + tk_w.pack() + SciAI = ScienceAI(tx,rx,tracker) # instantiate science AI instance tracker.bind_to_updates(SciAI.tracker_callback) # call this func on obj updates while True: for packet in rx: # stream packets from the server tracker.rx(packet) # Update the tracker with new information + tk_w.delete(tk.ALL) # clear canvas + for oid,odata in tracker.objects.items(): + try: + x = odata['x']/150 + z = odata['z']/150# - tracker.player_ship['z'] + #print("(%d,%d)"%(x,z)) + tk_w.create_rectangle(x,z,x+2,z+2,fill='blue') + except: pass + tk_root.update() + + + + #pp.pprint(packet) #pp.pprint(tracker.objects) + #print("") + #print("name\t enemy?\t side?") + for oid,odata in tracker.objects.items(): + try: + print(odata["name"],"\t",odata["enemy?"],"\t",odata["side?"]) + except: pass #for k,v in tracker.player_ship.items(): # if k in ["science-target","scanning-id","scanning-progress"]: # print(k,v) From dc8a1fe61360f685e4e2136be6c111731c740076 Mon Sep 17 00:00:00 2001 From: Nicholas Hammond Date: Tue, 15 Jan 2019 20:46:31 -0600 Subject: [PATCH 9/9] Update README.rst --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index b37fb6f..ae20e3f 100644 --- a/README.rst +++ b/README.rst @@ -3,6 +3,8 @@ libdiana A library for talking to Artemis SBS servers. +Big thanks to https://artemis-nerds.github.io/protocol-docs/ for making this project easier to update. + .. image:: https://badge.fury.io/py/libdiana.svg :target: http://badge.fury.io/py/libdiana