#!/usr/bin/env python # # Copyright Alex McBride 2009. # # This file is part of pyinsim. # # pyinsim is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # pyinsim is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with pyinsim. If not, see . # """An InSim module for the Python programming language. """ # Dependencies. import socket import threading import struct import math import re # This is going to be a big list when I finally write it. #__all__ = [''] # Constants. _INSIM_BUFFER_SIZE = 1024 INSIM_TCP = socket.SOCK_STREAM INSIM_UDP = socket.SOCK_DGRAM PYINSIM_VERSION = '1.5.5' _IP_PATTERN = r'\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2\ [0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25\ [0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b' _ENCODING_MAP = {'L': 'cp1252', 'G': 'iso8859_7', 'C': 'cp1251', 'J': 'Shift-JIS', 'E': 'iso8859_2', 'T': 'iso8859_9', 'B': 'iso8859_13', 'H': 'cp950', 'S': 'cp936', 'K': 'cp949'} _ESCAPE_MAP = {'v': '|', 'a': '*', 'c': ':', 'd': '\\', 's': '/', 'q': '?', 't': '"', 'l': '<', 'r': '>', '^': '^'} MAX_PORT = 65535 MIN_PORT = 1 ADMIN_LEN = 16 # Enum for close reason. CLOSE_REQUEST = 0 CLOSE_LFS = 1 # Enum for pyinsim events EVT_CLOSE = 253 EVT_ERROR = 254 EVT_TIMEOUT = 255 # Enum for OutReceiver mode. OUT_INSIM = 0 OUT_OUTSIM = 1 OUT_OUTGAUGE = 2 # Enum for packet-types ISP_NONE = 0 ISP_ISI = 1 ISP_VER = 2 ISP_TINY = 3 ISP_SMALL = 4 ISP_STA = 5 ISP_SCH = 6 ISP_SFP = 7 ISP_SCC = 8 ISP_CPP = 9 ISP_ISM = 10 ISP_MSO = 11 ISP_III = 12 ISP_MST = 13 ISP_MTC = 14 ISP_MOD = 15 ISP_VTN = 16 ISP_RST = 17 ISP_NCN = 18 ISP_CNL = 19 ISP_CPR = 20 ISP_NPL = 21 ISP_PLP = 22 ISP_PLL = 23 ISP_LAP = 24 ISP_SPX = 25 ISP_PIT = 26 ISP_PSF = 27 ISP_PLA = 28 ISP_CCH = 29 ISP_PEN = 30 ISP_TOC = 31 ISP_FLG = 32 ISP_PFL = 33 ISP_FIN = 34 ISP_RES = 35 ISP_REO = 36 ISP_NLP = 37 ISP_MCI = 38 ISP_MSX = 39 ISP_MSL = 40 ISP_CRS = 41 ISP_BFN = 42 ISP_AXI = 43 ISP_AXO = 44 ISP_BTN = 45 ISP_BTC = 46 ISP_BTT = 47 ISP_RIP = 48 ISP_SSH = 49 ISP_OUTGAUGE = 50 ISP_OUTSIM = 51 # InSim Relay packets IRP_HLR = 252 IRP_HOS = 253 IRP_SEL = 254 IRP_ERR = 255 # Enum for IS_TINY sub-type TINY_NONE = 0 TINY_VER = 1 TINY_CLOSE = 2 TINY_PING = 3 TINY_REPLY = 4 TINY_VTC = 5 TINY_SCP = 6 TINY_SST = 7 TINY_GTH = 8 TINY_MPE = 9 TINY_ISM = 10 TINY_REN = 11 TINY_CLR = 12 TINY_NCN = 13 TINY_NPL = 14 TINY_RES = 15 TINY_NLP = 16 TINY_MCI = 17 TINY_REO = 18 TINY_RST = 19 TINY_AXI = 20 TINY_AXC = 21 TINY_RIP = 22 # Enum for IS_SMALL sub-type SMALL_NONE = 0 SMALL_SSP = 1 SMALL_SSG = 2 SMALL_VTA = 3 SMALL_TMS = 4 SMALL_STP = 5 SMALL_RTP = 6 SMALL_NLI = 7 # Bit flags for ISI Flags ISF_RES_0 = 1 ISF_RES_1 = 2 ISF_LOCAL = 4 ISF_MSO_COLS = 8 ISF_NLP = 16 ISF_MCI = 32 # Enum for IS_MSO UserType. MSO_SYSTEM = 0 MSO_USER = 1 MSO_PREFIX = 2 MSO_O = 3 # Enum for IS_MSL Sound. SND_SILENT = 0 SND_MESSAGE = 1 SND_SYSMESSAGE = 2 SND_INVALIDKEY = 3 SND_ERROR = 4 # Enum for IS_VTN Action. VOTE_NONE = 0 VOTE_END = 1 VOTE_RESTART = 2 VOTE_QUALIFY = 3 # Enum for IS_PLA Fact. PITLANE_EXIT = 0 PITLANE_ENTER = 1 PITLANE_NO_PURPOSE = 2 PITLANE_DT = 3 PITLANE_SG = 4 # Enum for IS_STA InGameCam. VIEW_FOLLOW = 0 VIEW_HELI = 1 VIEW_CAM = 2 VIEW_DRIVER = 3 VIEW_CUSTOM = 4 VIEW_ANOTHER = 255 # Enum for IS_CNL Reason. LEAVR_DISCO = 0 LEAVR_TIMEOUT = 1 LEAVR_LOSTCONN = 2 LEAVR_KICKED = 3 LEAVR_BANNED = 4 LEAVR_SECURITY = 5 # Enum for IS_PEN Penalty. PENALTY_NONE = 0 PENALTY_DT = 1 PENALTY_DT_VALID = 2 PENALTY_SG = 3 PENALTY_SG_VALID = 4 PENALTY_30 = 5 PENALTY_45 = 6 # Enum for IS_PEN Reason. PENR_UNKNOWN = 1 PENR_ADMIN = 2 PENR_WRONG_WAY = 3 PENR_FALSE_START = 4 PENR_SPEEDING = 5 PENR_STOP_SHORT = 6 PENR_STOP_LATE = 7 # Enum for IS_PIT Tyres. TYRE_R1 = 0 TYRE_R2 = 1 TYRE_R3 = 2 TYRE_R4 = 3 TYRE_ROAD_SUPER = 4 TYRE_ROAD_NORMAL = 5 TYRE_HYBRID = 6 TYRE_KNOBBLY = 7 TYRE_NOT_CHANGED = 255 # Bit flags for IS_STA Flags. ISS_GAME = 1 ISS_REPLAY = 2 ISS_PAUSED = 4 ISS_SHIFTU = 8 ISS_SHIFTU_HIGH = 16 ISS_SHIFTU_FOLLOW = 32 ISS_SHIFTU_NO_OPT = 64 ISS_SHOW_2D = 128 ISS_FRONT_END = 256 ISS_MULTI = 512 ISS_MPSPEEDUP = 1024 ISS_WINDOWED = 2048 ISS_SOUND_MUTE = 4096 ISS_VIEW_OVERRIDE = 8192 ISS_VISIBLE = 16384 # Bit flags for IS_PIT Work. PSE_NOTHING = 1 PSE_STOP = 2 PSE_FR_DAM = 4 PSE_FR_WHL = 8 PSE_LE_FR_DAM = 16 PSE_LE_FR_WHL = 32 PSE_RI_FR_DAM = 64 PSE_RI_FR_WHL = 128 PSE_RE_DAM = 256 PSE_RE_WHL = 512 PSE_LE_RE_DAM = 1024 PSE_LE_RE_WHL = 2048 PSE_RI_RE_DAM = 4096 PSE_RI_RE_WHL = 8192 PSE_BODY_MINOR = 16384 PSE_BODY_MAJOR = 32768 PSE_SETUP = 65536 PSE_REFUEL = 131072 PSE_NUM = 262144 # Bit flags for IS_NPL Flags. PIF_SWAPSIDE = 1 PIF_RESERVED_2 = 2 PIF_RESERVED_4 = 4 PIF_AUTOGEARS = 8 PIF_SHIFTER = 16 PIF_RESERVED_32 = 32 PIF_HELP_B = 64 PIF_AXIS_CLUTCH = 128 PIF_INPITS = 256 PIF_AUTOCLUTCH = 512 PIF_MOUSE = 1024 PIF_KB_NO_HELP = 2048 PIF_KB_STABILISED = 4096 PIF_CUSTOM_VIEW = 8192 # Bit flags for IS_RES Confirm. CONF_MENTIONED = 1 CONF_CONFIRMED = 2 CONF_PENALTY_DT = 4 CONF_PENALTY_SG = 8 CONF_PENALTY_30 = 16 CONF_PENALTY_45 = 32 CONF_DID_NOT_PIT = 64 CONF_DISQ = (CONF_PENALTY_DT | CONF_PENALTY_SG | CONF_DID_NOT_PIT) CONF_TIME = (CONF_PENALTY_30 | CONF_PENALTY_45) # Bit flags for IS_RST Flags. HOSTF_CAN_VOTE = 1 HOSTF_CAN_SELECT = 2 HOSTF_MID_RACE = 32 HOSTF_MUST_PIT = 64 HOSTF_CAN_RESET = 128 HOSTF_FCV = 256 HOSTF_CRUISE = 512 # Bit flags for CompCar Info. CCI_BLUE = 1 CCI_YELLOW = 2 CCI_LAG = 32 CCI_FIRST = 64 CCI_LAST = 128 # Recomended area for IS_BTN T, L, W, H IS_X_MIN = 0 IS_X_MAX = 110 IS_Y_MIN = 30 IS_Y_MAX = 170 # Enum for IS_BFN SubT BFN_DEL_BTN = 0 BFN_CLEAR = 1 BFN_USER_CLEAR = 2 BFN_REQUEST = 3 INST_ALWAYS_ON = 128 # Bit flags for IS_BTN BStyle. ISB_C1 = 1 ISB_C2 = 2 ISB_C4 = 4 ISB_CLICK = 8 ISB_LIGHT = 16 ISB_DARK = 32 ISB_LEFT = 64 ISB_RIGHT = 128 # Bit flags for BTN CFlags. ISB_LMB = 1 ISB_RMB = 2 ISB_CTRL = 4 ISB_SHIFT = 8 # Enum for Weather. WEA_BRIGHTCLEAR = 0 WEA_OVERCAST = 1 WEA_CLOUDYSUNSETDUSK = 2 # Enum for Wind. WND_NONE = 0 WND_LOW = 1 WND_HIGH = 2 # Enum for IS_FLG Flag FLG_BLUE = 1 FLG_YELLOW = 2 # Enum for IS_FLG OffOn. FLG_ON = 1 FLG_OFF = 0 # Enum for IS_RIP Error. RIP_OK = 0 RIP_ALREADY = 1 RIP_DEDICATED = 2 RIP_WRONG_MODE = 3 RIP_NOT_REPLAY = 4 RIP_CORRUPTED = 5 RIP_NOT_FOUND = 6 RIP_UNLOADABLE = 7 RIP_DEST_OOB = 8 RIP_UNKNOWN = 9 RIP_USER = 10 RIP_OOS = 11 # Enum for IS_RIP Options. RIPOPT_LOOP = 1 RIPOPT_SKINS = 2 # Enum for IS_SSH Error. SSH_OK = 0 SSH_DEDICATED = 1 SSH_CORRUPTED = 2 SSH_NO_SAVE = 3 # Bit flags for IS_NPL SetF. SETF_SYMM_WHEELS = 1 SETF_TC_ENABLE = 2 SETF_ABS_ENABLE = 4 # Colour constants. COL_BLACK = '^0' COL_RED = '^1' COL_LIGHT_GREEN = '^2' COL_YELLOW = '^3' COL_BLUE = '^4' COL_PURPLE = '^5' COL_LIGHT_BLUE = '^6' COL_WHITE = '^7' COL_DARK_GREEN = '^8' COL_ORIGINAL = '^9' # Bit flags for OutGaugePack Flags. OG_TURBO = 8192 OG_KM = 16384 OG_BAR = 32768 # Bit flags OutGaugePack DashLights and ShowLights DL_SHIFT = 1 DL_FULLBEAM = 2 DL_HANDBRAKE = 4 DL_PITSPEED = 8 DL_TC = 16 DL_SIGNAL_L = 32 DL_SIGNAL_R = 64 DL_SIGNAL_ANY = 128 DL_OILWARN = 256 DL_BATTERY = 512 DL_ABS = 1024 DL_SPARE = 2048 DL_NUM= 4096 # InSim Relay host flags HOS_SPECPASS = 1 HOS_LICENSED = 2 HOS_S1 = 4 HOS_S2 = 8 # InSim Relay error codes IR_ERR_PACKET1 = 1 IR_ERR_PACKET2 = 2 IR_ERR_HOSTNAME = 3 IR_ERR_ADMIN = 4 IR_ERR_SPEC = 5 IR_ERR_NOSPEC = 6 # Functions. def checkIP(ipStr): """ Check if a string contains a valid IP address. @type ipStr: string @param ipStr: The host IP string to check. @rtype: boolean @return: True if the IP is valid. """ if ipStr.lower() == 'localhost': return True return re.match(_IP_PATTERN, ipStr) def checkPort(portStr): """Check if a string contains a valid port number. @type portStr: string @param portStr: The port string to check. @rtype: boolean @return: True if the port number is valid. """ if portStr.isdigit(): return int(portStr) >= MIN_PORT and int(portStr) <= MAX_PORT return False def checkAdmin(adminStr): """Check if a string contains a valid admin password. @type adminStr: string @param adminStr: The admin password string to check. @rtype: boolean @return: True if the admin password is valid. """ return len(adminStr) < ADMIN_LEN def parseCommand(mso, prefix): """Parse a command string from an MSO message (EG '!admin DarkTimes') and return it as a tuple. @type mso: IS_MSO @param mso: An IS_MSO packet, possibly containing a command. @type prefix: string @param prefix: The prefix (E.G. '!') the command begins with. @rtype: tuple @return: A tuple, with the command as the first element, any following command string as the second element, or () if no command could be parsed. """ msg = mso.Msg[mso.TextStart:] if msg.startswith(prefix): return msg[1:].split(' ', 1) return () def strToUnicode(str_, default='L', cols=True): """Convert a LFS encoded string to unicode. @type str_: string @param str_: The LFS encoded string. @type default: string @param default: The default encoding to start with (E.G. L, G, J etc..). @type cols: boolean @param cols: Whether to keep colour codes in the string. @rtype: unicode string @return: The string encoded to unicode. """ output = u'' accum = '' codec = _ENCODING_MAP[default] ctrlChar = False for c in str_: if ctrlChar: if c in _ENCODING_MAP: codec = _ENCODING_MAP[c] elif c in _ESCAPE_MAP: accum += _ESCAPE_MAP[c] elif cols: accum += '^' + c ctrlChar = False else: if c == '^': ctrlChar = True output += accum.decode(codec) accum = '' else: accum += c output += accum.decode(codec) return output def stripColours(lfsStr): """Strip colour codes (^1, ^3 etc..) from a string. @type lfsStr: string @param lfsStr: The string to strip. @rtype: string @return: The string sans colour codes. """ return re.sub(re.compile('\^[0-9]'), '', lfsStr) def speedToMps(speed): """Convert speed to meters per second. @type speed: number @param speed: The speed to convert. @rtype: number @return: Meters per second. """ return speed / 327.68 def speedToKph(speed): """Convert speed to kilometers per hour. @type speed: number @param speed: The speed to convert. @rtype: number @return: Kilometers per hour. """ return speed / 91.02 def mpsToKph(mps): """Convert meters per second to kilometers per hour. @type mps: number @param mps: Meters per second. @rtype: number @return: Kilometers per hour. """ return mps * 3.6 def speedToMph(speed): """Convert speed to miles per hour. @type speed: number @param speed: The speed to convert. @rtype: number @return: Miles per hour. """ return speed / 146.486067 def mpsToMph(mps): """Convert meters per second to miles per hour. @type mps: number @param mps: Meters per second. @rtype: number @return: Miles per hour. """ return mps * 2.23 def metersToMiles(meters): """Convert meters to miles. @type meters: number @param meters: The meters to convert. @rtype: number @return: Miles. """ return meters / 1609.344 def metersToKilometers(meters): """Convert meters to kilometers. @type meters: number @param meters: The meters to convert. @rtype: number @return: Kilometers. """ return meters / 1000 def directionToDegrees(direction): """Convert direction to degrees. @type direction: number @param direction: The direction to convert. @rtype: number @return: Degrees. """ return direction / 182.04 def lengthToMetres(length): """Convert length to meters. @type length: number @param length: The length to convert. @rtype: number @return: Meters """ return length / 65536.0 def distance(a = (0, 0, 0), b = (0, 0, 0)): """Calculate the distance between two points. @type a: tuple @param a: The points (X, Y, Z) coordinates. @type b: tuple @param b: The points (X, Y, Z) coordinates. @rtype: number @return: The distance. """ return math.sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]) + (b[2] - a[2]) * (b[2] - a[2])) def radiansToDegrees(radians): """Convert radians to degrees. @type radians: number @param radians: The radians to convert. @rtype: number @return: Degrees. """ return radians * (180.0 / math.pi); def radiansToRpm(radians): """Convert radians to RPM. @type radians: number @param radians: The radians to convert. @rtype: number @return: RPM. """ return radians * (60.0 / (2.0 * math.pi)); # Broken. #def packetXml(packetType, name=None, xmlPath=None, xmlStr=None): # """Create a packet object from XML data. The following is an example of # XML which this function will convert into a Packet object. # # # # # 1 # 0 # pass # Pyinsim # $ # # # # In order to create a Packet from this XML data, you would call the function # with the following paramaters. # # isi = pyinsim.packetXml(pyinsim.ISP_ISI, "init", pathToXmlFile) # # Args: # packetType - The packet type from the ISP_* enumeration. # name - The name attribute of the packet in the XML. # xmlPath - A path to an XML file or... # xmlStr - Alternatively a string of XML data. # # Returns: # A packet object populated from the XML data. # # """ # # This function almost feels a little obsolete, it works well, but it # # could be a lot better. # if xmlPath is None: # doc = minidom.parseString(xmlStr) # else: # doc = minidom.parse(xmlPath) # # packet = Packet(packetType) # for baseNode in doc.getElementsByTagName('packet'): # node = str(baseNode.getAttribute('name')) # if node == name: # for attribute in packet.iterkeys(): # for subNode in baseNode.getElementsByTagName(attribute): # data = subNode.firstChild.data # if type(packet[attribute]) == str: # packet[attribute] = str(data) # elif type(packet[attribute ]) == int: # packet[attribute] = int(data) # elif type(packet[attribute ]) == float: # packet[attribute] = float(data) # elif type(packet[attribute ]) == chr: # packet[attribute] = chr(data) # return packet def insimConnect(host='localhost', port=29999, **values): """Short-hand for initailising an InSim connection. It creates an Insim object, calls its connect() method, and sends an ISI packet. >>> # Example. >>> insim = pyinsim.insimConnect('localhost', 29999, Admin='pass') @type host: string @param host: The host to connect to. @type port: number @param port: The port to connect to the host through. @type values: args @param values: The values to initailise the ISI packet with. @rtype: InSim @return: The initailised InSim object. """ insim = InSim() insim.connect(host, port) insim.send(ISP_ISI, **values) return insim def startOutReceiver(host='localhost', port=30000, mode=OUT_INSIM, timeout=10.0): """Short-hand for initailising an OutReceiver(). @type host: string @param host: The host to connect to. @type port: number @param port: The port to connect to the host through. @type mode: enum @param mode: The mode from the OUT_ enumeration. @type timeout: number @param timeout: The seconds to wait before timing out. @rtype: OutReceiver @return: The initailise OutReceiver object. """ out = OutReceiver(mode, timeout) out.start(host, port) return out def time(ms): """Convert milliseconds into hours, minutes, seconds and thousanths. @type ms: number @param ms: The time in milliseconds @rtype: tuple @return: A tuple containing the (hours, mins, secs, thousandths). """ h = int(math.floor(ms / 3600000)) m = int(math.floor(ms / 1000 / 60) % 60) s = int(math.floor(ms / 1000) % 60) t = int(ms % 1000) return (h, m, s, t) def timeStr(ms, hours=False): """Convert milliseconds into a formatted time string (EG h:m:s.t). @type ms: number @param ms: The time in milliseconds. @type hours: boolean @param hours: Set to True to force use of the hours componant of the time. @rtype: string @return: The formatted time string (E.G. hh:mm:ss.ttt) """ h, m, s, t = time(ms) if h or hours: return '%d.%.2d:%.2d.%.3d' % (h, m, s, t) else: return '%d:%.2d.%.3d' % (m, s, t) # Helper functions. def _packetSize(data): """Get the size of the packet.""" if len(data): return ord(data[0]) return 0 def _packetType(data): """Get the packet type from the ISP_ enumeration.""" if len(data): return ord(data[1]) return ISP_NONE def _tinyType(data): """Get the tiny type from the TINY_ enumeration.""" return ord(data[3]) def _eatNullChars(str_): """Remove NULL terminating line characters.""" return str_.rstrip('\000') # InSim packets. class IS_ISI: """InSim Init - packet to initialise the InSim system """ def __init__(self, ReqI=0, UDPPort=0, Flags=0, Prefix=' ', Interval=0, Admin='', IName=''): """Initialise the packet. @type ReqI: number @param ReqI: If non-zero LFS will send an IS_VER packet @type UDPPort: number @param UDPPort: Port for UDP replies from LFS (0 to 65535) @type Flags: number @param Flags: ISF_ bit flags for options @type Prefix: string @param Prefix: Special host message prefix character @type Interval: number @param Interval: Time in ms between NLP or MCI (0 = none) @type Admin: string @param Admin: Admin password (if set in LFS) @type IName: string @param IName: A short name for your program """ self.Size = 44 self.Type = ISP_ISI self.ReqI = ReqI self.Zero = 0 self.UDPPort = UDPPort self.Flags = Flags self.Sp0 = 0 self.Prefix = Prefix self.Interval = Interval self.Admin = Admin self.IName = IName def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBHHBcH16s16s', self.Size, self.Type, self.ReqI, self.Zero, self.UDPPort, self.Flags, self.Sp0, self.Prefix, self.Interval, self.Admin, self.IName) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.UDPPort, self.Flags, self.Sp0, self.Prefix, self.Interval, self.Admin, self.IName = struct.unpack('BBBBHHBcH16s16s', data) self.Admin = _eatNullChars(self.Admin) self.IName = _eatNullChars(self.IName) class IS_VER: """VERsion. It is advisable to request version information as soon as you have connected, to avoid problems when connecting to a host with a later or earlier version. You will be sent a version packet on connection if you set ReqI in the IS_ISI packet. """ def __init__(self, ReqI=0, Version='', Product='', InSimVer=0): """Initialise the packet. @type ReqI: number @param ReqI: ReqI as received in the request packet @type Version: string @param Version: LFS version, e.g. 0.3G @type Product: string @param Product: Product : DEMO or S1 @type InSimVer: number @param InSimVer: InSim Version : increased when InSim packets change """ self.Size = 20 self.Type = ISP_VER self.ReqI = ReqI self.Zero = 0 self.Version = Version self.Product = Product self.InSimVer = InSimVer def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB8s6sH', self.Size, self.Type, self.ReqI, self.Zero, self.Version, self.Product, self.InSimVer) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.Version, self.Product, self.InSimVer = struct.unpack('BBBB8s6sH', data) self.Version = _eatNullChars(self.Version) self.Product = _eatNullChars(self.Product) class IS_TINY: """General purpose packet. """ def __init__(self, ReqI=0, SubT=0): """Initialise the packet. @type ReqI: number @param ReqI: zero (0) unless in response to a request. @type SubT: enum @param SubT: subtype from TINY_ enumeration (e.g. TINY_REN) """ self.Size = 4 self.Type = ISP_TINY self.ReqI = ReqI self.SubT = SubT def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB', self.Size, self.Type, self.ReqI, self.SubT) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.SubT = struct.unpack('BBBB', data) class IS_SMALL: """General purpose packet. """ def __init__(self, ReqI=0, SubT=0, UVal=0): """Initialise the packet. @type ReqI: number @param ReqI: zero (0) unless in response to a request. @type SubT: enum @param SubT: subtype from SMALL_ enumeration (e.g. SMALL_SSP) @type UVal: number @param UVal: value (e.g. for SMALL_SSP this would be the OutSim packet rate) """ self.Size = 8 self.Type = ISP_SMALL self.ReqI = ReqI self.SubT = SubT self.UVal = UVal def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBI', self.Size, self.Type, self.ReqI, self.SubT, self.UVal) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.SubT, self.UVal = struct.unpack('BBBBI', data) class IS_STA: """STAte packet, sent whenever the data in the packet changes. To request this packet send a TINY with a ReqI of non-zero and a SubT of TINY_STA. """ def __init__(self, ReqI=0, ReplaySpeed=0.0, Flags=0, InGameCam=0, ViewPLID=0, NumP=0, NumConns=0, NumFinished=0, RaceInProg=0, QualMins=0, RaceLaps=0, Track='', Weather=0, Wind=0): """Initialise the packet. @param ReqI: ReqI if replying to a request packet @param ReplaySpeed: 4-byte float - 1.0 is normal speed @param Flags: ISS_ state flags @param InGameCam: Which type of camera is selected @param ViewPLID: Unique ID of viewed player (0 = none) @param NumP: Number of players in race @param NumConns: Number of connections including host @param NumFinished: Number finished or qualified @param RaceInProg: 0 - no race / 1 - race / 2 - qualifying @param QualMins: Qualifing minutes @param RaceLaps: see "RaceLaps" near the top of this document @param Track: short name for track e.g. FE2R @param Weather: options from WEA_ @param Wind: options from WND_ """ self.Size = 28 self.Type = ISP_STA self.ReqI = ReqI self.Zero = 0 self.ReplaySpeed = ReplaySpeed self.Flags = Flags self.InGameCam = InGameCam self.ViewPLID = ViewPLID self.NumP = NumP self.NumConns = NumConns self.NumFinished = NumFinished self.RaceInProg = RaceInProg self.QualMins = QualMins self.RaceLaps = RaceLaps self.Spare2 = 0 self.Spare3 = 0 self.Track = Track self.Weather = Weather self.Wind = Wind def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBfHBBBBBBBBBB6sBB', self.Size, self.Type, self.ReqI, self.Zero, self.ReplaySpeed, self.Flags, self.InGameCam, self.ViewPLID, self.NumP, self.NumConns, self.NumFinished, self.RaceInProg, self.QualMins, self.RaceLaps, self.Spare2, self.Spare3, self.Track, self.Weather, self.Wind) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.ReplaySpeed, self.Flags, self.InGameCam, self.ViewPLID, self.NumP, self.NumConns, self.NumFinished, self.RaceInProg, self.QualMins, self.RaceLaps, self.Spare2, self.Spare3, self.Track, self.Weather, self.Wind = struct.unpack('BBBBfHBBBBBBBBBB6sBB', data) self.Track = _eatNullChars(self.Track) class IS_SCH: """Single CHaracter You can send individual key presses to LFS with the IS_SCH packet. For standard keys (e.g. V and H) you should send a capital letter. This does not work with some keys like F keys, arrows or CTRL keys. You can also use IS_MST with the /press /shift /ctrl /alt commands. """ def __init__(self, ReqI=0, CharB=0, Flags=0): """Initialise the packet. @param ReqI: 0 @param CharB: key to press @param Flags: bit 0 : SHIFT / bit 1 : CTRL """ self.Size = 8 self.Type = ISP_SCH self.ReqI = ReqI self.Zero = 0 self.CharB = CharB self.Flags = Flags self.Spare2 = 0 self.Spare3 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB', self.Size, self.Type, self.ReqI, self.Zero, self.CharB, self.Flags, self.Spare2, self.Spare3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.CharB, self.Flags, self.Spare2, self.Spare3 = struct.unpack('BBBBBBBB', data) class IS_SFP: """State Flags Pack. Send this packet to set the game state. Other states must be set by using key-presses or slash commands. """ def __init__(self, ReqI=0, Flag=0, OffOn=0): """Initialise the packet. @param ReqI: ReqI as received in the request packet @param Flag: ISS_* state flags @param OffOn: 0 = off / 1 = on """ self.Size = 8 self.Type = ISP_SFP self.ReqI = ReqI self.Zero = 0 self.Flag = Flag self.OffOn = OffOn self.Sp3 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBHBB', self.Size, self.Type, self.ReqI, self.Zero, self.Flag, self.OffOn, self.Sp3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.Flag, self.OffOn, self.Sp3 = struct.unpack('BBBBHBB', data) class IS_SCC: """Set Car Camera - Simplified camera packet (not SHIFT+U mode) """ def __init__(self, ReqI=0, ViewPLID=0, InGameCam=0): """Initialise the packet. @param ReqI: 0 @param ViewPLID: UniqueID of player to view @param InGameCam: InGameCam (as reported in StatePack) """ self.Size = 8 self.Type = ISP_SCC self.ReqI = ReqI self.Zero = 0 self.ViewPLID = ViewPLID self.InGameCam = InGameCam self.Sp2 = 0 self.Sp3 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB', self.Size, self.Type, self.ReqI, self.Zero, self.ViewPLID, self.InGameCam, self.Sp2, self.Sp3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.ViewPLID, self.InGameCam, self.Sp2, self.Sp3 = struct.unpack('BBBBBBBB', data) class IS_CPP: """Cam Pos Pack - Full camera packet (in car or SHIFT+U mode) """ def __init__(self, ReqI=0, Pos=[0, 0, 0], H=0, P=0, R=0, ViewPLID=0, InGameCam=0, FOV=0.0, Time=0, Flags=0): """Initialise the packet. @param ReqI: instruction : 0 / or reply : ReqI as received in the TINY_SCP @param Pos: Position vector @param H: heading - 0 points along Y axis @param P: pitch - 0 means looking at horizon @param R: roll - 0 means no roll @param ViewPLID: Unique ID of viewed player (0 = none) @param InGameCam: InGameCam (as reported in StatePack) @param FOV: FOV in degrees @param Time: Time to get there (0 means instant + reset) @param Flags: state flags from ISS_ """ self.Size = 32 self.Type = ISP_CPP self.ReqI = ReqI self.Zero = 0 self.Pos = Pos self.H = H self.P = P self.R = R self.ViewPLID = ViewPLID self.InGameCam = InGameCam self.FOV = FOV self.Time = Time self.Flags = Flags def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBiiiHHHBBfHH', self.Size, self.Type, self.ReqI, self.Zero, self.Pos[0], self.Pos[1], self.Pos[2], self.H, self.P, self.R, self.ViewPLID, self.InGameCam, self.FOV, self.Time, self.Flags) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.Pos[0], self.Pos[1], self.Pos[2], self.H, self.P, self.R, self.ViewPLID, self.InGameCam, self.FOV, self.Time, self.Flags = struct.unpack('BBBBiiiHHHBBfHH', data) class IS_ISM: """InSim Multi LFS will send this packet when a host is started or joined. """ def __init__(self, ReqI=0, Host=0, HName=''): """Initialise the packet. @param ReqI: usually 0 / or if a reply : ReqI as received in the TINY_ISM @param Host: 0 = guest / 1 = host @param HName: the name of the host joined or started """ self.Size = 40 self.Type = ISP_ISM self.ReqI = ReqI self.Zero = 0 self.Host = Host self.Sp1 = 0 self.Sp2 = 0 self.Sp3 = 0 self.HName = HName def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB32s', self.Size, self.Type, self.ReqI, self.Zero, self.Host, self.Sp1, self.Sp2, self.Sp3, self.HName) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.Host, self.Sp1, self.Sp2, self.Sp3, self.HName = struct.unpack('BBBBBBBB32s', data) self.HName = _eatNullChars(self.HName) class IS_MSO: """MSg Out - system messages and user messages """ def __init__(self, ReqI=0, UCID=0, PLID=0, UserType=0, TextStart=0, Msg=''): """Initialise the packet. @param ReqI: 0 @param UCID: connection's unique id (0 = host) @param PLID: player's unique id (if zero, use UCID) @param UserType: flags from MSO_ enumeration @param TextStart: first character of the actual text (after player name) @param Msg: message """ self.Size = 136 self.Type = ISP_MSO self.ReqI = ReqI self.Zero = 0 self.UCID = UCID self.PLID = PLID self.UserType = UserType self.TextStart = TextStart self.Msg = Msg def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB128s', self.Size, self.Type, self.ReqI, self.Zero, self.UCID, self.PLID, self.UserType, self.TextStart, self.Msg) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.UCID, self.PLID, self.UserType, self.TextStart, self.Msg = struct.unpack('BBBBBBBB128s', data) self.Msg = _eatNullChars(self.Msg) class IS_III: """InsIm Info - /i message from user to host's InSim """ def __init__(self, ReqI=0, UCID=0, PLID=0, Msg=''): """Initialise the packet. @param ReqI: 0 @param UCID: connection's unique id (0 = host) @param PLID: player's unique id (if zero, use UCID) @param Msg: message """ self.Size = 72 self.Type = ISP_III self.ReqI = ReqI self.Zero = 0 self.UCID = UCID self.PLID = PLID self.Sp2 = 0 self.Sp3 = 0 self.Msg = Msg def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB64s', self.Size, self.Type, self.ReqI, self.Zero, self.UCID, self.PLID, self.Sp2, self.Sp3, self.Msg) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.UCID, self.PLID, self.Sp2, self.Sp3, self.Msg = struct.unpack('BBBBBBBB64s', data) self.Msg = _eatNullChars(self.Msg) class IS_MST: """MSg Type - send to LFS to type message or command """ def __init__(self, ReqI=0, Msg=''): """Initialise the packet. @param ReqI: 0 @param Msg: message (64 characters) """ self.Size = 68 self.Type = ISP_MST self.ReqI = ReqI self.Zero = 0 self.Msg = Msg def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB64s', self.Size, self.Type, self.ReqI, self.Zero, self.Msg) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.Msg = struct.unpack('BBBB64s', data) self.Msg = _eatNullChars(self.Msg) class IS_MTC: """Msg To Connection - hosts only - send to a connection or a player """ def __init__(self, ReqI=0, UCID=0, PLID=0, Msg=''): """Initialise the packet. @param ReqI: 0 @param UCID: connection's unique id (0 = host) @param PLID: player's unique id (if zero, use UCID) @param Msg: Message (64 characters) """ self.Size = 72 self.Type = ISP_MTC self.ReqI = ReqI self.Zero = 0 self.UCID = UCID self.PLID = PLID self.Sp2 = 0 self.Sp3 = 0 self.Msg = Msg def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB64s', self.Size, self.Type, self.ReqI, self.Zero, self.UCID, self.PLID, self.Sp2, self.Sp3, self.Msg) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.UCID, self.PLID, self.Sp2, self.Sp3, self.Msg = struct.unpack('BBBBBBBB64s', data) self.Msg = _eatNullChars(self.Msg) class IS_MOD: """MODe : send to LFS to change screen mode """ def __init__(self, ReqI=0, Bits16=0, RR=0, Width=0, Height=0): """Initialise the packet. @param ReqI: 0 @param Bits16: set to choose 16-bit @param RR: refresh rate - zero for default @param Width: 0 means go to window @param Height: 0 means go to window """ self.Size = 20 self.Type = ISP_MOD self.ReqI = ReqI self.Zero = 0 self.Bits16 = Bits16 self.RR = RR self.Width = Width self.Height = Height def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBiiii', self.Size, self.Type, self.ReqI, self.Zero, self.Bits16, self.RR, self.Width, self.Height) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.Bits16, self.RR, self.Width, self.Height = struct.unpack('BBBBiiii', data) class IS_VTN: """VoTe Notify """ def __init__(self, ReqI=0, UCID=0, Action=0): """Initialise the packet. @param ReqI: 0 @param UCID: connection's unique id @param Action: Vote action from VOTE_ """ self.Size = 8 self.Type = ISP_VTN self.ReqI = ReqI self.Zero = 0 self.UCID = UCID self.Action = Action self.Spare2 = 0 self.Spare3 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB', self.Size, self.Type, self.ReqI, self.Zero, self.UCID, self.Action, self.Spare2, self.Spare3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.UCID, self.Action, self.Spare2, self.Spare3 = struct.unpack('BBBBBBBB', data) class IS_RST: """Race STart """ def __init__(self, ReqI=0, RaceLaps=0, QualMins=0, NumP=0, Track='', Weather=0, Wind=0, Flags=0, NumNodes=0, Finish=0, Split1=0, Split2=0, Split3=0): """Initialise the packet. @param ReqI: 0 unless this is a reply to an TINY_RST request @param RaceLaps: 0 if qualifying @param QualMins: 0 if race @param NumP: number of players in race @param Track: short track name @param Weather: weather @param Wind: wind @param Flags: race flags from HOSTF_ @param NumNodes: total number of nodes in the path @param Finish: node index - finish line @param Split1: node index - split 1 @param Split2: node index - split 2 @param Split3: node index - split 3 """ self.Size = 28 self.Type = ISP_RST self.ReqI = ReqI self.Zero = 0 self.RaceLaps = RaceLaps self.QualMins = QualMins self.NumP = NumP self.Spare = 0 self.Track = Track self.Weather = Weather self.Wind = Wind self.Flags = Flags self.NumNodes = NumNodes self.Finish = Finish self.Split1 = Split1 self.Split2 = Split2 self.Split3 = Split3 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB6sBBHHHHHH', self.Size, self.Type, self.ReqI, self.Zero, self.RaceLaps, self.QualMins, self.NumP, self.Spare, self.Track, self.Weather, self.Wind, self.Flags, self.NumNodes, self.Finish, self.Split1, self.Split2, self.Split3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.RaceLaps, self.QualMins, self.NumP, self.Spare, self.Track, self.Weather, self.Wind, self.Flags, self.NumNodes, self.Finish, self.Split1, self.Split2, self.Split3 = struct.unpack('BBBBBBBB6sBBHHHHHH', data) self.Track = _eatNullChars(self.Track) class IS_NCN: """New ConN """ def __init__(self, ReqI=0, UCID=0, UName='', PName='', Admin=0, Total=0, Flags=0): """Initialise the packet. @param ReqI: 0 unless this is a reply to a TINY_NCN request @param UCID: new connection's unique id (0 = host) @param UName: username @param PName: nickname @param Admin: 1 if admin @param Total: number of connections including host @param Flags: bit 2 : remote """ self.Size = 56 self.Type = ISP_NCN self.ReqI = ReqI self.UCID = UCID self.UName = UName self.PName = PName self.Admin = Admin self.Total = Total self.Flags = Flags self.Sp3 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB24s24sBBBB', self.Size, self.Type, self.ReqI, self.UCID, self.UName, self.PName, self.Admin, self.Total, self.Flags, self.Sp3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.UCID, self.UName, self.PName, self.Admin, self.Total, self.Flags, self.Sp3 = struct.unpack('BBBB24s24sBBBB', data) self.UName = _eatNullChars(self.UName) self.PName = _eatNullChars(self.PName) class IS_CNL: """ConN Leave """ def __init__(self, ReqI=0, UCID=0, Reason=0, Total=0): """Initialise the packet. @param ReqI: 0 @param UCID: unique id of the connection which left @param Reason: leave reason from LEAVR_ @param Total: number of connections including host """ self.Size = 8 self.Type = ISP_CNL self.ReqI = ReqI self.UCID = UCID self.Reason = Reason self.Total = Total self.Sp2 = 0 self.Sp3 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB', self.Size, self.Type, self.ReqI, self.UCID, self.Reason, self.Total, self.Sp2, self.Sp3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.UCID, self.Reason, self.Total, self.Sp2, self.Sp3 = struct.unpack('BBBBBBBB', data) class IS_CPR: """Conn Player Rename """ def __init__(self, ReqI=0, UCID=0, PName='', Plate=''): """Initialise the packet. @param ReqI: 0 @param UCID: unique id of the connection @param PName: new name @param Plate: number plate """ self.Size = 36 self.Type = ISP_CPR self.ReqI = ReqI self.UCID = UCID self.PName = PName self.Plate = Plate def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB24s8s', self.Size, self.Type, self.ReqI, self.UCID, self.PName, self.Plate) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.UCID, self.PName, self.Plate = struct.unpack('BBBB24s8s', data) self.PName = _eatNullChars(self.PName) class IS_NPL: """New PLayer joining race (if PLID already exists, then leaving pits) """ def __init__(self, ReqI=0, PLID=0, UCID=0, PType=0, Flags=0, PName='', Plate='', CName='', SName='', Tyres=[0, 0, 0, 0], H_Mass=0, H_TRes=0, Model=0, Pass=0, Spare=0, SetF=0, NumP=0): """Initialise the packet. @param ReqI: 0 unless this is a reply to an TINY_NPL request @param PLID: player's newly assigned unique id @param UCID: connection's unique id @param PType: bit 0 : female / bit 1 : AI / bit 2 : remote @param Flags: player flags from PIF_ @param PName: nickname @param Plate: number plate - NO ZERO AT END! @param CName: car name @param SName: skin name - MAX_CAR_TEX_NAME @param Tyres: compounds @param H_Mass: added mass (kg) @param H_TRes: intake restriction @param Model: driver model @param Pass: passengers byte @param SetF: Flags from SETF_ @param NumP: number in race (same when leaving pits, 1 more if new) """ self.Size = 76 self.Type = ISP_NPL self.ReqI = ReqI self.PLID = PLID self.UCID = UCID self.PType = PType self.Flags = Flags self.PName = PName self.Plate = Plate self.CName = CName self.SName = SName self.Tyres = Tyres self.H_Mass = H_Mass self.H_TRes = H_TRes self.Model = Model self.Pass = Pass self.Spare = 0 self.SetF = SetF self.NumP = NumP self.Sp2 = 0 self.Sp3 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBH24s8s4s16sBBBBBBBBiBBBB', self.Size, self.Type, self.ReqI, self.PLID, self.UCID, self.PType, self.Flags, self.PName, self.Plate, self.CName, self.SName, self.Tyres[0], self.Tyres[1], self.Tyres[2], self.Tyres[3], self.H_Mass, self.H_TRes, self.Model, self.Pass, self.Spare, self.SetF, self.NumP, self.Sp2, self.Sp3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID, self.UCID, self.PType, self.Flags, self.PName, self.Plate, self.CName, self.SName, self.Tyres[0], self.Tyres[1], self.Tyres[2], self.Tyres[3], self.H_Mass, self.H_TRes, self.Model, self.Pass, self.Spare, self.SetF, self.NumP, self.Sp2, self.Sp3 = struct.unpack('BBBBBBH24s8s4s16sBBBBBBBBiBBBB', data) self.PName = _eatNullChars(self.PName) self.CName = _eatNullChars(self.CName) self.SName = _eatNullChars(self.SName) class IS_PLP: """PLayer Pits (go to settings - stays in player list) """ def __init__(self, ReqI=0, PLID=0): """Initialise the packet. @param ReqI: 0 @param PLID: player's unique id """ self.Size = 4 self.Type = ISP_PLP self.ReqI = ReqI self.PLID = PLID def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB', self.Size, self.Type, self.ReqI, self.PLID) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID = struct.unpack('BBBB', data) class IS_PLL: """PLayer Leave race (spectate - removed from player list) """ def __init__(self, ReqI=0, PLID=0): """Initialise the packet. @param ReqI: 0 @param PLID: player's unique id """ self.Size = 4 self.Type = ISP_PLL self.ReqI = ReqI self.PLID = PLID def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB', self.Size, self.Type, self.ReqI, self.PLID) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID = struct.unpack('BBBB', data) class IS_LAP: """LAP time """ def __init__(self, ReqI=0, PLID=0, LTime=0, ETime=0, LapsDone=0, Flags=0, Penalty=0, NumStops=0): """Initialise the packet. @param ReqI: 0 @param PLID: player's unique id @param LTime: lap time (ms) @param ETime: total time (ms) @param LapsDone: laps completed @param Flags: player flags from PIF_ @param Penalty: current penalty value from PENALTY_ @param NumStops: number of pit stops """ self.Size = 20 self.Type = ISP_LAP self.ReqI = ReqI self.PLID = PLID self.LTime = LTime self.ETime = ETime self.LapsDone = LapsDone self.Flags = Flags self.Sp0 = 0 self.Penalty = Penalty self.NumStops = NumStops self.Sp3 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBIIHHBBBB', self.Size, self.Type, self.ReqI, self.PLID, self.LTime, self.ETime, self.LapsDone, self.Flags, self.Sp0, self.Penalty, self.NumStops, self.Sp3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID, self.LTime, self.ETime, self.LapsDone, self.Flags, self.Sp0, self.Penalty, self.NumStops, self.Sp3 = struct.unpack('BBBBIIHHBBBB', data) class IS_SPX: """SPlit X time """ def __init__(self, ReqI=0, PLID=0, STime=0, ETime=0, Split=0, Penalty=0, NumStops=0): """Initialise the packet. @param ReqI: 0 @param PLID: player's unique id @param STime: split time (ms) @param ETime: total time (ms) @param Split: split number 1, 2, 3 @param Penalty: current penalty value from PENALTY_ @param NumStops: number of pit stops """ self.Size = 16 self.Type = ISP_SPX self.ReqI = ReqI self.PLID = PLID self.STime = STime self.ETime = ETime self.Split = Split self.Penalty = Penalty self.NumStops = NumStops self.Sp3 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBIIBBBB', self.Size, self.Type, self.ReqI, self.PLID, self.STime, self.ETime, self.Split, self.Penalty, self.NumStops, self.Sp3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID, self.STime, self.ETime, self.Split, self.Penalty, self.NumStops, self.Sp3 = struct.unpack('BBBBIIBBBB', data) class IS_PIT: """PIT stop (stop at pit garage) """ def __init__(self, ReqI=0, PLID=0, LapsDone=0, Flags=0, Penalty=0, NumStops=0, Tyres=[0, 0, 0, 0], Work=0): """Initialise the packet. @param ReqI: 0 @param PLID: player's unique id @param LapsDone: laps completed @param Flags: player flags from PIF_ @param Penalty: current penalty value from PENALTY_ @param NumStops: number of pit stops @param Tyres: tyres changed from TYRE_ @param Work: pit work from PSE_ flags """ self.Size = 24 self.Type = ISP_PIT self.ReqI = ReqI self.PLID = PLID self.LapsDone = LapsDone self.Flags = Flags self.Sp0 = 0 self.Penalty = Penalty self.NumStops = NumStops self.Sp3 = 0 self.Tyres = Tyres self.Work = Work self.Spare = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBHHBBBBBBBBII', self.Size, self.Type, self.ReqI, self.PLID, self.LapsDone, self.Flags, self.Sp0, self.Penalty, self.NumStops, self.Sp3, self.Tyres[0], self.Tyres[1], self.Tyres[2], self.Tyres[3], self.Work, self.Spare) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID, self.LapsDone, self.Flags, self.Sp0, self.Penalty, self.NumStops, self.Sp3, self.Tyres[0], self.Tyres[1], self.Tyres[2], self.Tyres[3], self.Work, self.Spare = struct.unpack('BBBBHHBBBBBBBBII', data) class IS_PSF: """Pit Stop Finished """ def __init__(self, ReqI=0, PLID=0, STime=0): """Initialise the packet. @param ReqI: 0 @param PLID: player's unique id @param STime: stop time (ms) """ self.Size = 12 self.Type = ISP_PSF self.ReqI = ReqI self.PLID = PLID self.STime = STime self.Spare = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBII', self.Size, self.Type, self.ReqI, self.PLID, self.STime, self.Spare) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID, self.STime, self.Spare = struct.unpack('BBBBII', data) class IS_PLA: """Pit LAne """ def __init__(self, ReqI=0, PLID=0, Fact=0): """Initialise the packet. @param ReqI: 0 @param PLID: player's unique id @param Fact: pit lane facts from PITLANE_ """ self.Size = 8 self.Type = ISP_PLA self.ReqI = ReqI self.PLID = PLID self.Fact = Fact self.Sp1 = 0 self.Sp2 = 0 self.Sp3 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB', self.Size, self.Type, self.ReqI, self.PLID, self.Fact, self.Sp1, self.Sp2, self.Sp3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID, self.Fact, self.Sp1, self.Sp2, self.Sp3 = struct.unpack('BBBBBBBB', data) class IS_CCH: """Camera CHange """ def __init__(self, ReqI=0, PLID=0, Camera=0): """Initialise the packet. @param ReqI: 0 @param PLID: player's unique id @param Camera: view identifier from VIEW_ """ self.Size = 8 self.Type = ISP_CCH self.ReqI = ReqI self.PLID = PLID self.Camera = Camera self.Sp1 = 0 self.Sp2 = 0 self.Sp3 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB', self.Size, self.Type, self.ReqI, self.PLID, self.Camera, self.Sp1, self.Sp2, self.Sp3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID, self.Camera, self.Sp1, self.Sp2, self.Sp3 = struct.unpack('BBBBBBBB', data) class IS_PEN: """PENalty (given or cleared) """ def __init__(self, ReqI=0, PLID=0, OldPen=0, NewPen=0, Reason=0): """Initialise the packet. @param ReqI: 0 @param PLID: player's unique id @param OldPen: old penalty value from PENALTY_ @param NewPen: new penalty value PENALTY_ @param Reason: penalty reason from PENR_ """ self.Size = 8 self.Type = ISP_PEN self.ReqI = ReqI self.PLID = PLID self.OldPen = OldPen self.NewPen = NewPen self.Reason = Reason self.Sp3 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB', self.Size, self.Type, self.ReqI, self.PLID, self.OldPen, self.NewPen, self.Reason, self.Sp3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID, self.OldPen, self.NewPen, self.Reason, self.Sp3 = struct.unpack('BBBBBBBB', data) class IS_TOC: """Take Over Car """ def __init__(self, ReqI=0, PLID=0, OldUCID=0, NewUCID=0): """Initialise the packet. @param ReqI: 0 @param PLID: player's unique id @param OldUCID: old connection's unique id @param NewUCID: new connection's unique id """ self.Size = 8 self.Type = ISP_TOC self.ReqI = ReqI self.PLID = PLID self.OldUCID = OldUCID self.NewUCID = NewUCID self.Sp2 = 0 self.Sp3 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB', self.Size, self.Type, self.ReqI, self.PLID, self.OldUCID, self.NewUCID, self.Sp2, self.Sp3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID, self.OldUCID, self.NewUCID, self.Sp2, self.Sp3 = struct.unpack('BBBBBBBB', data) class IS_FLG: """FLaG (yellow or blue flag changed) """ def __init__(self, ReqI=0, PLID=0, OffOn=0, Flag=0, CarBehind=0): """Initialise the packet. @param ReqI: 0 @param PLID: player's unique id @param OffOn: 0 = off / 1 = on @param Flag: flag given from FLG_ @param CarBehind: unique id of obstructed player """ self.Size = 8 self.Type = ISP_FLG self.ReqI = ReqI self.PLID = PLID self.OffOn = OffOn self.Flag = Flag self.CarBehind = CarBehind self.Sp3 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB', self.Size, self.Type, self.ReqI, self.PLID, self.OffOn, self.Flag, self.CarBehind, self.Sp3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID, self.OffOn, self.Flag, self.CarBehind, self.Sp3 = struct.unpack('BBBBBBBB', data) class IS_PFL: """Player FLags (help flags changed) """ def __init__(self, ReqI=0, PLID=0, Flags=0): """Initialise the packet. @param ReqI: 0 @param PLID: player's unique id @param Flags: player flags from PIF_ """ self.Size = 8 self.Type = ISP_PFL self.ReqI = ReqI self.PLID = PLID self.Flags = Flags self.Spare = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBHH', self.Size, self.Type, self.ReqI, self.PLID, self.Flags, self.Spare) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID, self.Flags, self.Spare = struct.unpack('BBBBHH', data) class IS_FIN: """FINished race notification (not a final result - use IS_RES) """ def __init__(self, ReqI=0, PLID=0, TTime=0, BTime=0, NumStops=0, Confirm=0, LapsDone=0, Flags=0): """Initialise the packet. @param ReqI: 0 @param PLID: player's unique id (0 = player left before result was sent) @param TTime: race time (ms) @param BTime: best lap (ms) @param NumStops: number of pit stops @param Confirm: confirmation flags from CONF_ @param LapsDone: laps completed @param Flags: player flags from PIF_ """ self.Size = 20 self.Type = ISP_FIN self.ReqI = ReqI self.PLID = PLID self.TTime = TTime self.BTime = BTime self.SpA = 0 self.NumStops = NumStops self.Confirm = Confirm self.SpB = 0 self.LapsDone = LapsDone self.Flags = Flags def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBIIBBBBHH', self.Size, self.Type, self.ReqI, self.PLID, self.TTime, self.BTime, self.SpA, self.NumStops, self.Confirm, self.SpB, self.LapsDone, self.Flags) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID, self.TTime, self.BTime, self.SpA, self.NumStops, self.Confirm, self.SpB, self.LapsDone, self.Flags = struct.unpack('BBBBIIBBBBHH', data) class IS_RES: """RESult (qualify or confirmed finish) """ def __init__(self, ReqI=0, PLID=0, UName='', PName='', Plate='', CName='', TTime=0, BTime=0, NumStops=0, Confirm=0, LapsDone=0, Flags=0, ResultNum=0, NumRes=0, PSeconds=0): """Initialise the packet. @param ReqI: 0 unless this is a reply to a TINY_RES request @param PLID: player's unique id (0 = player left before result was sent) @param UName: username @param PName: nickname @param Plate: number plate - NO ZERO AT END! @param CName: skin prefix @param TTime: race time (ms) @param BTime: best lap (ms) @param NumStops: number of pit stops @param Confirm: confirmation flags CONF_ @param LapsDone: laps completed @param Flags: player flags from PIF_ @param ResultNum: finish or qualify pos (0 = win / 255 = not added to table) @param NumRes: total number of results (qualify doesn't always add a new one) @param PSeconds: penalty time in seconds (already included in race time) """ self.Size = 84 self.Type = ISP_RES self.ReqI = ReqI self.PLID = PLID self.UName = UName self.PName = PName self.Plate = Plate self.CName = CName self.TTime = TTime self.BTime = BTime self.SpA = 0 self.NumStops = NumStops self.Confirm = Confirm self.SpB = 0 self.LapsDone = LapsDone self.Flags = Flags self.ResultNum = ResultNum self.NumRes = NumRes self.PSeconds = PSeconds def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB24s24s8s4sIIBBBBHHBBH', self.Size, self.Type, self.ReqI, self.PLID, self.UName, self.PName, self.Plate, self.CName, self.TTime, self.BTime, self.SpA, self.NumStops, self.Confirm, self.SpB, self.LapsDone, self.Flags, self.ResultNum, self.NumRes, self.PSeconds) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID, self.UName, self.PName, self.Plate, self.CName, self.TTime, self.BTime, self.SpA, self.NumStops, self.Confirm, self.SpB, self.LapsDone, self.Flags, self.ResultNum, self.NumRes, self.PSeconds = struct.unpack('BBBB24s24s8s4sIIBBBBHHBBH', data) self.UName = _eatNullChars(self.UName) self.PName = _eatNullChars(self.PName) self.CName = _eatNullChars(self.CName) class IS_REO: """REOrder (when race restarts after qualifying) """ def __init__(self, ReqI=0, NumP=0, PLID=[]): """Initialise the packet. @param ReqI: 0 unless this is a reply to an TINY_REO request @param NumP: number of players in race @param PLID: all PLIDs in new order """ self.Size = 36 self.Type = ISP_REO self.ReqI = ReqI self.NumP = NumP self.PLID = PLID def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB32B', self.Size, self.Type, self.ReqI, self.NumP, *self.PLID) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.NumP = struct.unpack('BBBB', data[:4]) self.PLID = struct.unpack('32B', data[4:]) class NodeLap: """Car info in 6 bytes - there is an array of these in the NLP (below) """ def __init__(self, Node=0, Lap=0, PLID=0, Position=0): """Initialise the packet. @param Node: current path node @param Lap: current lap @param PLID: player's unique id @param Position: current race position : 0 = unknown, 1 = leader, etc... """ self.Node = Node self.Lap = Lap self.PLID = PLID self.Position = Position def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('HHBB', self.Node, self.Lap, self.PLID, self.Position) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Node, self.Lap, self.PLID, self.Position = struct.unpack('HHBB', data) class IS_NLP: """Node and Lap Packet - variable size @type NodeLaps: list @ivar NodeLaps: A list of NodeLap packets. """ def __init__(self, ReqI=0, NumP=0): """Initialise the packet. @param ReqI: 0 unless this is a reply to an TINY_NLP request @param NumP: number of players in race """ self.Size = 4 self.Type = ISP_NLP self.ReqI = ReqI self.NumP = NumP self.NodeLaps = [] def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB', self.Size, self.Type, self.ReqI, self.NumP) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.NumP = struct.unpack('BBBB', data[:4]) lim = self.Size if self.NumP % 2 == 1: lim -= 2 offset = 4 while offset < lim: nl = NodeLap() nl.unpack(data[offset:offset + 6]) self.NodeLaps.append(nl) offset += 6 class CompCar: """Car info in 28 bytes - there is an array of these in the MCI (below) """ def __init__(self, Node=0, Lap=0, PLID=0, Position=0, Info=0, X=0, Y=0, Z=0, Speed=0, Direction=0, Heading=0, AngVel=0): """Initialise the packet. @param Node: current path node @param Lap: current lap @param PLID: player's unique id @param Position: current race position : 0 = unknown, 1 = leader, etc... @param Info: flags and other info - see below @param X: X map (65536 = 1 metre) @param Y: Y map (65536 = 1 metre) @param Z: Z alt (65536 = 1 metre) @param Speed: speed (32768 = 100 m/s) @param Direction: direction of car's motion : 0 = world y direction, 32768 = 180 deg @param Heading: direction of forward axis : 0 = world y direction, 32768 = 180 deg @param AngVel: signed, rate of change of heading : (16384 = 360 deg/s) """ self.Node = Node self.Lap = Lap self.PLID = PLID self.Position = Position self.Info = Info self.Sp3 = 0 self.X = X self.Y = Y self.Z = Z self.Speed = Speed self.Direction = Direction self.Heading = Heading self.AngVel = AngVel def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('HHBBBBiiiHHHh', self.Node, self.Lap, self.PLID, self.Position, self.Info, self.Sp3, self.X, self.Y, self.Z, self.Speed, self.Direction, self.Heading, self.AngVel) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Node, self.Lap, self.PLID, self.Position, self.Info, self.Sp3, self.X, self.Y, self.Z, self.Speed, self.Direction, self.Heading, self.AngVel = struct.unpack('HHBBBBiiiHHHh', data) class IS_MCI: """Multi Car Info - if more than 8 in race then more than one of these is sent @type CompCars: list @ivar CompCars: A list of CompCar packets. """ def __init__(self, ReqI=0, NumC=0): """Initialise the packet. @param ReqI: 0 unless this is a reply to an TINY_MCI request @param NumC: number of valid CompCar structs in this packet """ self.Size = 4 self.Type = ISP_MCI self.ReqI = ReqI self.NumC = NumC self.CompCars = [] def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB', self.Size, self.Type, self.ReqI, self.NumC) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.NumC = struct.unpack('BBBB', data[:4]) offset = 4 while offset < self.Size: cc = CompCar() cc.unpack(data[offset:offset + 28]) self.CompCars.append(cc) offset += 28 class IS_MSX: """MSg eXtended - like MST but longer (not for commands) """ def __init__(self, ReqI=0, Msg=''): """Initialise the packet. @param ReqI: 0 @param Msg: Message (96 characters) """ self.Size = 100 self.Type = ISP_MSX self.ReqI = ReqI self.Zero = 0 self.Msg = Msg def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB96s', self.Size, self.Type, self.ReqI, self.Zero, self.Msg) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.Msg = struct.unpack('BBBB96s', data) self.Msg = _eatNullChars(self.Msg) class IS_MSL: """MSg Local - message to appear on local computer only """ def __init__(self, ReqI=0, Sound=0, Msg=''): """Initialise the packet. @param ReqI: 0 @param Sound: Sound from SND_ enumeration. @param Msg: Message """ self.Size = 132 self.Type = ISP_MSL self.ReqI = ReqI self.Sound = Sound self.Msg = Msg def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB128s', self.Size, self.Type, self.ReqI, self.Sound, self.Msg) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Sound, self.Msg = struct.unpack('BBBB128s', data) self.Msg = _eatNullChars(self.Msg) class IS_CRS: """Car ReSet """ def __init__(self, ReqI=0, PLID=0): """Initialise the packet. @param ReqI: 0 @param PLID: player's unique id """ self.Size = 4 self.Type = ISP_CRS self.ReqI = ReqI self.PLID = PLID def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB', self.Size, self.Type, self.ReqI, self.PLID) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID = struct.unpack('BBBB', data) class IS_BFN: """Button FunctioN - delete buttons / receive button requests """ def __init__(self, ReqI=0, SubT=0, UCID=0, ClickID=0, Inst=0): """Initialise the packet. @param ReqI: 0 @param SubT: subtype, from BFN_* enumeration @param UCID: connection to send to or from (0 = local / 255 = all) @param ClickID: ID of button to delete (if SubT is BFN_DEL_BTN) @param Inst: used internally by InSim """ self.Size = 8 self.Type = ISP_BFN self.ReqI = ReqI self.SubT = SubT self.UCID = UCID self.ClickID = ClickID self.Inst = Inst self.Sp3 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB', self.Size, self.Type, self.ReqI, self.SubT, self.UCID, self.ClickID, self.Inst, self.Sp3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.SubT, self.UCID, self.ClickID, self.Inst, self.Sp3 = struct.unpack('BBBBBBBB', data) class IS_AXI: """AutoX Info """ def __init__(self, ReqI=0, AXStart=0, NumCP=0, NumO=0, LName=''): """Initialise the packet. @param ReqI: 0 unless this is a reply to an TINY_AXI request @param AXStart: autocross start position @param NumCP: number of checkpoints @param NumO: number of objects @param LName: the name of the layout last loaded (if loaded locally) """ self.Size = 40 self.Type = ISP_AXI self.ReqI = ReqI self.Zero = 0 self.AXStart = AXStart self.NumCP = NumCP self.NumO = NumO self.LName = LName def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBH32s', self.Size, self.Type, self.ReqI, self.Zero, self.AXStart, self.NumCP, self.NumO, self.LName) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.AXStart, self.NumCP, self.NumO, self.LName = struct.unpack('BBBBBBH32s', data) self.LName = _eatNullChars(self.LName) class IS_AXO: """AutoX Object """ def __init__(self, ReqI=0, PLID=0): """Initialise the packet. @param ReqI: 0 @param PLID: player's unique id """ self.Size = 4 self.Type = ISP_AXO self.ReqI = ReqI self.PLID = PLID def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ struct.pack('BBBB', self.Size, self.Type, self.ReqI, self.PLID) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.PLID = struct.unpack('BBBB', data) class IS_BTN: """BuTtoN - button header - followed by 0 to 240 characters """ def __init__(self, ReqI=0, UCID=0, ClickID=0, Inst=0, BStyle=0, TypeIn=0, L=0, T=0, W=0, H=0, Text=''): """Initialise the packet. @param ReqI: non-zero (returned in IS_BTC and IS_BTT packets) @param UCID: connection to display the button (0 = local / 255 = all) @param ClickID: button ID (0 to 239) @param Inst: some extra flags from INST_ @param BStyle: button style flags from ISB_ @param TypeIn: max chars to type in @param L: left: 0 - 200 @param T: top: 0 - 200 @param W: width: 0 - 200 @param H: height: 0 - 200 @param Text: 0 to 240 characters of text """ self.Size = 252 self.Type = ISP_BTN self.ReqI = ReqI self.UCID = UCID self.ClickID = ClickID self.Inst = Inst self.BStyle = BStyle self.TypeIn = TypeIn self.L = L self.T = T self.W = W self.H = H self.Text = Text def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBBBBBB240s', self.Size, self.Type, self.ReqI, self.UCID, self.ClickID, self.Inst, self.BStyle, self.TypeIn, self.L, self.T, self.W, self.H, self.Text) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.UCID, self.ClickID, self.Inst, self.BStyle, self.TypeIn, self.L, self.T, self.W, self.H, self.Text = struct.unpack('BBBBBBBBBBBB240s', data) self.Text = _eatNullChars(self.Text) class IS_BTC: """BuTton Click - sent back when user clicks a button """ def __init__(self, ReqI=0, UCID=0, ClickID=0, Inst=0, CFlags=0): """Initialise the packet. @param ReqI: ReqI as received in the IS_BTN @param UCID: connection that clicked the button (zero if local) @param ClickID: button identifier originally sent in IS_BTN @param Inst: used internally by InSim @param CFlags: button click flags from ISB_ """ self.Size = 8 self.Type = ISP_BTC self.ReqI = ReqI self.UCID = UCID self.ClickID = ClickID self.Inst = Inst self.CFlags = CFlags self.Sp3 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB', self.Size, self.Type, self.ReqI, self.UCID, self.ClickID, self.Inst, self.CFlags, self.Sp3) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.UCID, self.ClickID, self.Inst, self.CFlags, self.Sp3 = struct.unpack('BBBBBBBB', data) class IS_BTT: """BuTton Type - sent back when user types into a text entry button """ def __init__(self, ReqI=0, UCID=0, ClickID=0, Inst=0, TypeIn=0, Text=''): """Initialise the packet. @param ReqI: ReqI as received in the IS_BTN @param UCID: connection that typed into the button (zero if local) @param ClickID: button identifier originally sent in IS_BTN @param Inst: used internally by InSim @param TypeIn: from original button specification @param Text: typed text, zero to TypeIn specified in IS_BTN """ self.Size = 104 self.Type = ISP_BTT self.ReqI = ReqI self.UCID = UCID self.ClickID = ClickID self.Inst = Inst self.TypeIn = TypeIn self.Sp3 = 0 self.Text = Text def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB96s', self.Size, self.Type, self.ReqI, self.UCID, self.ClickID, self.Inst, self.TypeIn, self.Sp3, self.Text) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.UCID, self.ClickID, self.Inst, self.TypeIn, self.Sp3, self.Text = struct.unpack('BBBBBBBB96s', data) self.Text = _eatNullChars(self.Text) class IS_RIP: """Replay Information Packet """ def __init__(self, ReqI=0, Error=0, MPR=0, Paused=0, Options=0, CTime=0, TTime=0, RName=''): """Initialise the packet. @param ReqI: request : non-zero / reply : same value returned @param Error: 0 or 1 = OK / options from RIP_ @param MPR: 0 = SPR / 1 = MPR @param Paused: request : pause on arrival / reply : paused state @param Options: various options from RIPOPT_ @param CTime: (hundredths) request : destination / reply : position @param TTime: (hundredths) request : zero / reply : replay length @param RName: zero or replay name: last byte must be zero """ self.Size = 76 self.Type = ISP_RIP self.ReqI = ReqI self.Error = Error self.MPR = MPR self.Paused = Paused self.Options = Options self.Sp3 = 0 self.CTime = CTime self.TTime = TTime self.RName = RName def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBBHH64s', self.Size, self.Type, self.ReqI, self.Error, self.MPR, self.Paused, self.Options, self.Sp3, self.CTime, self.TTime, self.RName) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Error, self.MPR, self.Paused, self.Options, self.Sp3, self.CTime, self.TTime, self.RName = struct.unpack('BBBBBBBBHH64s', data) self.RName = _eatNullChars(self.RName) class IS_SSH: """ScreenSHot """ def __init__(self, ReqI=0, Error=0, BMP=''): """Initialise the packet. @param ReqI: request : non-zero / reply : same value returned @param Error: 0 = OK / other values from SSH_ @param BMP: name of screenshot file: last byte must be zero """ self.Size = 40 self.Type = ISP_SSH self.ReqI = ReqI self.Error = Error self.Sp0 = 0 self.Sp1 = 0 self.Sp2 = 0 self.Sp3 = 0 self.BMP = BMP def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBBBBBB32s', self.Size, self.Type, self.ReqI, self.Error, self.Sp0, self.Sp1, self.Sp2, self.Sp3, self.BMP) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Error, self.Sp0, self.Sp1, self.Sp2, self.Sp3, self.BMP = struct.unpack('BBBBBBBB32s', data) self.BMP = _eatNullChars(self.BMP) class IR_HLR: """InSim Relay Host List Request """ def __init__(self, ReqI=0): """Initialise the packet. @param ReqI: request : non-zero / reply : same value returned """ self.Size = 4 self.Type = IRP_HLR self.ReqI = ReqI self.Sp0 = 0 def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB', self.Size, self.Type, self.ReqI, self.Sp0) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Sp0 = struct.unpack('BBBB', data) class HInfo: """Host info in 40 bytes - each IR_HOS packet has an array of these """ def __init__(self, HName='', Track='', Flags=0, NumConns=0): """Initialise the packet. @param HName: host name @param Track: short track name, e.g., BL1R @param Flags: host flags @param NumConns: number of active connections to the host """ self.HName = HName self.Track = Track self.Flags = Flags self.NumConns = NumConns def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('32s6sBB', self.HName, self.Track, self.Flags, self.NumConns) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.HName, self.Track, self.Flags, self.NumConns = struct.unpack('32s6sBB', data) class IR_HOS: """InSim Relay HOSt List """ def __init__(self, ReqI=0, NumHosts=0): """Initialise the packet. @param ReqI: request : non-zero / reply : same value returned @param NumHosts: number of hosts available via InSim Relay """ self.Size = 4 self.Type = IRP_HLR self.ReqI = ReqI self.NumHosts = NumHosts self.Hosts = [] def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB', self.Size, self.Type, self.ReqI, self.NumHosts) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.NumHosts = struct.unpack('BBBB', data[:4]) offset = 4 while offset < self.NumHosts * 40: h = HInfo() h.unpack(data[offset:offset + 40]) self.Hosts.append(h) offset += 40 class IR_SEL: """InSim Relay host SELection """ def __init__(self, ReqI=0, HName='', Admin='', Spec=''): """Initialise the packet. @param ReqI: request : non-zero / reply : same value returned @param HName: selected host to receive InSim packets from @param Admin: admin password for selected host @param Spec: spectator password for selected host """ self.Size = 68 self.Type = IRP_SEL self.ReqI = ReqI self.Zero = 0 self.HName = HName self.Admin = Admin self.Spec = Spec def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB32s16s16s', self.Size, self.Type, self.ReqI, self.Zero, self.HName, self.Admin, self.Spec) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.Zero, self.HName, self.Admin, self.Spec = struct.unpack('BBBB32s16s16s', data) class IR_ERR: """InSim Relay host SELection """ def __init__(self, ReqI=0, ErrNo=0): """Initialise the packet. @param ReqI: request : non-zero / reply : same value returned @param ErrNo: error code returned from relay """ self.Size = 4 self.Type = IRP_HLR self.ReqI = ReqI self.ErrNo = ErrNo def pack(self): """Pack the packet values into a binary formatted string. @rtype: string @return: A binary formatted string. """ return struct.pack('BBBB', self.Size, self.Type, self.ReqI, self.ErrNo) def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Size, self.Type, self.ReqI, self.ErrNo = struct.unpack('BBBB', data) class OutSimPack: """External Motion simulator support The user's car in multiplayer or the viewed car in single player or single player replay can output information to a motion system while viewed from an internal view. This can be controlled by 5 lines in the cfg.txt file : >>> OutSim Mode 0 :0-off 1-driving 2-driving+replay >>> OutSim Delay 1 :minimum delay between packets (100ths of a sec) >>> OutSim IP 0.0.0.0 :IP address to send the UDP packet >>> OutSim Port 0 :IP port >>> OutSim ID 0 :if not zero, adds an identifier to the packet """ def __init__(self, Time=0, AngVel=[0.0, 0.0, 0.0], Heading=0.0, Pitch=0.0, Roll=0.0, Accel=[0.0, 0.0, 0.0], Vel=[0.0, 0.0, 0.0], Pos=[0, 0, 0], ID=0): """ Initailise the packet. @param Time: time in milliseconds (to check order) @param AngVel: 3 floats, angular velocity vector @param Heading: anticlockwise from above (Z) @param Pitch: anticlockwise from right (X) @param Roll: anticlockwise from front (Y) @param Accel: 3 floats X, Y, Z @param Vel: 3 floats X, Y, Z @param Pos: 3 ints X, Y, Z (1m = 65536) @param ID: optional: only if OutSim ID is specified """ self.Time = Time self.AngVel = AngVel self.Heading = Heading self.Pitch = Pitch self.Roll = Roll self.Accel = Accel self.Vel = Vel self.Pos = Pos self.ID = ID def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Time, self.AngVel[0], self.AngVel[1], self.AngVel[2], self.Heading, self.Pitch, self.Roll, self.Accel[0], self.Accel[1], self.Accel[2], self.Vel[0], self.Vel[1], self.Vel[2], self.Pos[0], self.Pos[1], self.Pos[2] = struct.unpack('Iffffffffffffiii', data[:64]) # ID is only on if set in cfg.txt. if len(data) == 68: self.ID = struct.unpack('i', data[64:])[0] class OutGaugePack: """External dashboard support. The user's car in multiplayer or the viewed car in single player or single player replay can output information to a dashboard system while viewed from an internal view. This can be controlled by 5 lines in the cfg.txt file : >>> OutGauge Mode 0 :0-off 1-driving 2-driving+replay >>> OutGauge Delay 1 :minimum delay between packets (100ths of a sec) >>> OutGauge IP 0.0.0.0 :IP address to send the UDP packet >>> OutGauge Port 0 :IP port >>> OutGauge ID 0 :if not zero, adds an identifier to the packet """ def __init__(self, Time=0, Car='', Flags=0, Gear=0, Speed=0.0, RPM=0.0, Turbo=0.0, EngTemp=0.0, Fuel=0.0, OilPress=0.0, OilTemp=0.0, DashLights=0, ShowLights=0, Throttle=0.0, Brake=0.0, Clutch=0.0, Display1='', Display2='', ID=0): """Initailise the packet. @param Time: Time in milliseconds (to check order) @param Car: Car name @param Flags: Bit flags from OG_ enumeration @param Gear: Reverse: 0, Neutral: 1, Firest: 2 etc.. @param Speed: Meters per second @param RPM: RPM @param Turbo: BAR @param EngTemp: C @param Fuel: 0.0 to 1.0 @param OilPress: BAR @param OilTemp: C @param DashLights: Dash lights available (see DL_x below) @param ShowLights: Dash lights currently switched on @param Throttle: 0.0 to 1.0 @param Brake: 0.0 to 1.0 @param Clutch: 0.0 to 1.0 @param Display1: Usually fuel @param Display2: Usually settings @param ID: Options, if OutGauge ID specified """ self.Time = Time self.Car = Car self.Flags = Flags self.Gear = Gear self.SpareB = 0 self.Speed = Speed self.RPM = RPM self.Turbo = Turbo self.EngTemp = EngTemp self.Fuel = Fuel self.OilPress = OilPress self.OilTemp = OilTemp self.DashLights = DashLights self.ShowLights = ShowLights self.Throttle = Throttle self.Brake = Brake self.Clutch = Clutch self.Display1 = Display1 self.Display2 = Display2 self.ID = ID def unpack(self, data): """Unpack the packet data from a binary formatted string. @type data: string @param data: The packet data as a binary formatted string. """ self.Time, self.Car, self.Flags, self.Gear, self.SpareB, self.Speed, self.RPM, self.Turbo, self.EngTemp,self.Fuel, self.OilPress, self.OilTemp, self.DashLights, self.ShowLights, self.Throttle, self.Brake, self.Clutch, self.Display1,self.Display2 = struct.unpack('I4sHBBfffffffIIfff16s16s', data[:92]) # Check if ID has been set. if len(data) == 96: self.ID = struct.unpack('i', data[92:])[0] self.Car = _eatNullChars(self.Car) self.Display1 = _eatNullChars(self.Display1) self.Display2 = _eatNullChars(self.Display2) # Lookup packet-type from string. _PACKET_STRS = {'ISP_ISI': ISP_ISI, 'ISP_VER': ISP_VER, 'ISP_TINY': ISP_TINY, 'ISP_SMALL': ISP_SMALL, 'ISP_STA': ISP_STA, 'ISP_SCH': ISP_SCH, 'ISP_SFP': ISP_SFP, 'ISP_SCC': ISP_SCC, 'ISP_CPP': ISP_CPP, 'ISP_ISM': ISP_ISM, 'ISP_MSO': ISP_MSO, 'ISP_III': ISP_III, 'ISP_MST': ISP_MST, 'ISP_MTC': ISP_MTC, 'ISP_MOD': ISP_MOD, 'ISP_VTN': ISP_VTN, 'ISP_RST': ISP_RST, 'ISP_NCN': ISP_NCN, 'ISP_CNL': ISP_CNL, 'ISP_CPR': ISP_CPR, 'ISP_NPL': ISP_NPL, 'ISP_PLP': ISP_PLP, 'ISP_PLL': ISP_PLL, 'ISP_LAP': ISP_LAP, 'ISP_SPX': ISP_SPX, 'ISP_PIT': ISP_PIT, 'ISP_PSF': ISP_PSF, 'ISP_PLA': ISP_PLA, 'ISP_CCH': ISP_CCH, 'ISP_PEN': ISP_PEN, 'ISP_TOC': ISP_TOC, 'ISP_FLG': ISP_FLG, 'ISP_PFL': ISP_PFL, 'ISP_FIN': ISP_FIN, 'ISP_RES': ISP_RES, 'ISP_REO': ISP_REO, 'ISP_NLP': ISP_NLP, 'ISP_MCI': ISP_MCI, 'ISP_MSX': ISP_MSX, 'ISP_MSL': ISP_MSL, 'ISP_CRS': ISP_CRS, 'ISP_BFN': ISP_BFN, 'ISP_AXI': ISP_AXI, 'ISP_AXO': ISP_AXO, 'ISP_BTN': ISP_BTN, 'ISP_BTC': ISP_BTC, 'ISP_BTT': ISP_BTT, 'ISP_RIP': ISP_RIP, 'ISP_SSH': ISP_SSH, 'IRP_HLR': IRP_HLR, 'IRP_HOS': IRP_HOS, 'IRP_SEL': IRP_SEL, 'IRP_ERR': IRP_ERR,} # Associate packet-types with the packet classes. _PACKET_DEFS = {ISP_ISI: IS_ISI, ISP_VER: IS_VER, ISP_TINY: IS_TINY, ISP_SMALL: IS_SMALL, ISP_STA: IS_STA, ISP_SCH: IS_SCH, ISP_SFP: IS_SFP, ISP_SCC: IS_SCC, ISP_CPP: IS_CPP, ISP_ISM: IS_ISM, ISP_MSO: IS_MSO, ISP_III: IS_III, ISP_MST: IS_MST, ISP_MTC: IS_MTC, ISP_MOD: IS_MOD, ISP_VTN: IS_VTN, ISP_RST: IS_RST, ISP_NCN: IS_NCN, ISP_CNL: IS_CNL, ISP_CPR: IS_CPR, ISP_NPL: IS_NPL, ISP_PLP: IS_PLP, ISP_PLL: IS_PLL, ISP_LAP: IS_LAP, ISP_SPX: IS_SPX, ISP_PIT: IS_PIT, ISP_PSF: IS_PSF, ISP_PLA: IS_PLA, ISP_CCH: IS_CCH, ISP_PEN: IS_PEN, ISP_TOC: IS_TOC, ISP_FLG: IS_FLG, ISP_PFL: IS_PFL, ISP_FIN: IS_FIN, ISP_RES: IS_RES, ISP_REO: IS_REO, ISP_NLP: IS_NLP, ISP_MCI: IS_MCI, ISP_MSX: IS_MSX, ISP_MSL: IS_MSL, ISP_CRS: IS_CRS, ISP_BFN: IS_BFN, ISP_AXI: IS_AXI, ISP_AXO: IS_AXO, ISP_BTN: IS_BTN, ISP_BTC: IS_BTC, ISP_BTT: IS_BTT, ISP_RIP: IS_RIP, ISP_SSH: IS_SSH, IRP_HLR: IR_HLR, IRP_HOS: IR_HOS, IRP_SEL: IR_SEL, IRP_ERR: IR_ERR,} class _Buffer: """Class to manage the receive buffer. """ def __init__(self): """Initailise the _Buffer() object. """ self.__buffer = '' def receive(self, data): """Append received data onto the buffer. @type data: string @param data: Binary formatted string. """ self.__buffer += data def packets(self): """Get all completed packets currently in the buffer. @rtype: string @return: The next completed packet, as a binary formatted string. """ size = _packetSize(self.__buffer) while len(self.__buffer) and len(self.__buffer) >= size: packet = self.__buffer[:size] self.__buffer = self.__buffer[size:] yield packet size = _packetSize(self.__buffer) class InSim: """Class to manage an InSim connection with LFS. """ def __init__(self, conn=INSIM_TCP, timeout=10.0, name='default', use_relay=False): """Initialise the InSim object. @type conn: enum @param conn: Type of connection, either INSIM_TCP or INSIM_UDP. @type timeout: number @param timeout: Seconds to wait before timing out in UDP mode. @type name: string @param name: An optional name for the connection. """ self.__conn = conn self.__timeout = timeout self.__name = name self.__use_relay = use_relay self.__buffer = _Buffer() self.__connected = False self.__callbacks = {} self.__thread = threading.Thread(target=self.__receiveThread) self.__sock = None def getName(self): """Get the name of the InSim connection. @rtype: string @return: Name of the connection. """ return self.__name def setName(self, name): """Set the name of the InSim connection. @type name: string @param name: Name of the connection. """ self.__name = name def setTimeout(self, timeout): """Set the UDP timeout. @type timeout: number @param timeout: The timeout seconds. """ self.__timeout = timeout def getTimeout(self): """Get the UDP timeout. @rtype: number @return: The timeout seconds. """ return self.__timeout def isConnected(self): """Get if the socket is connected. @rtype: boolean @return: True if the socket is connected. """ return self.__connected def connect(self, host='localhost', port=29999, blocking=False): """ Connect to InSim on the specified host and port. Known Issues: In UDP mode no exception is raised if the connection to InSim fails, but instead an EVT_ERROR is raised after the first packet is sent. @type host: string @param host: Host where LFS is running. @type port: number @param port: Port to connect to LFS through. """ if not self.__connected: if not self.__use_relay: self.__sock = socket.socket(socket.AF_INET, self.__conn) if self.__conn == INSIM_UDP: self.__sock.settimeout(self.__timeout) self.__sock.connect((host, port)) self.__connected = True self.__thread.start() else: self.__sock = socket.socket(socket.AF_INET, INSIM_TCP) self.__sock.connect(('isrelay.lfs.net', 47474)) self.__connected = True self.__thread.start() def close(self): """Close the InSim connection. Sends a TINY_CLOSE before releasing the socket. """ self.__close() self.onInSimEvent(EVT_CLOSE, CLOSE_REQUEST) def __close(self): """Internal close which does not raise an InSimClosed event. """ if self.__connected: self.__connected = False self.send(ISP_TINY, SubT=TINY_CLOSE) self.__sock.close() def send(self, packetType, **values): """Send a packet to InSim. >>> # Example. >>> InSim.send(ISP_ISI, ReqI=1, Admin='password', IName='example') @type packetType: enum @param packetType: Type of packet to send, from the ISP_ enumeration. @type values: args @param values: Values to initialise the packet with. """ self.sendB(_PACKET_DEFS[packetType](**values).pack()) def sendB(self, data): """Send raw binary data to InSim. @type data: string @param data: The data to send, as a binary formatted string. """ self.__sock.send(data) def sendP(self, *packets): """Send one or more packets to InSim. @type packets: list @param packets: A list of packets to send. """ [self.sendB(packet.pack()) for packet in packets] def __receiveThread(self): """The internal receive thread. """ try: while self.__connected: data = self.__sock.recv(_INSIM_BUFFER_SIZE) if data: self.__buffer.receive(data) for packet in self.__buffer.packets(): self.onPacketReceived(packet) else: self.onInSimEvent(EVT_CLOSE, CLOSE_LFS) break except socket.timeout, err: self.onInSimEvent(EVT_TIMEOUT, self.__timeout) except Exception, err: self.onInSimEvent(EVT_ERROR, err) finally: self.__close() def onPacketReceived(self, data): """Virtual method called when a packet is received. Handles the keep alive pulse and raises packet events. @type data: string @param data: Packet data as a binary formatted string. """ packetType = _packetType(data) self.__keepAlive(packetType, data) # Raise packet event. if packetType in self.__callbacks: for callback, customPacket in self.__callbacks[packetType]: if customPacket: packet = customPacket() else: packet = _PACKET_DEFS[packetType]() packet.unpack(data) callback(self, packet) def __keepAlive(self, packetType, data): if packetType == ISP_TINY: if _tinyType(data) == TINY_NONE: self.sendB(data) def run(self): """Block the calling thread until the InSim connection has closed. """ self.__thread.join() def onInSimEvent(self, evtType, args): """Virtual method called when an InSim event is raised. @type evtType: enum @param evtType: The type of event to raise an event for. @type args: args @param args: Event arguments. """ if evtType in self.__callbacks: for callback, _ in self.__callbacks[evtType]: callback(self, args) def bind(self, evtType, callback, customPacket=None): """Bind a packet callback event-handler. @type evtType: enum @param evtType: Type of evt to bind. @type callback: function @param callback: Function to call when the event is raised. @type customPacket: object @param customPacket: A custom packet object to use when unpacking packet data. """ if not evtType in self.__callbacks: self.__callbacks[evtType] = [] self.__callbacks[evtType].append((callback, customPacket)) def unbind(self, evtType, callback): """Unbind a packet callback event-handler. @type evtType: enum @param evtType: Type of event to unbind. @type callback: function @param callback: Event-handler to unbind for this packet type. """ for i in range(len(self.__callbacks[evtType])): if self.__callbacks[evtType][i][0] == callback: del self.__callbacks[evtType][i] break def isBound(self, evtType, callback): """Get if a specific event-handler has been bound., @type evtType: enum @param evtType: Event type to check for. @type callback: function @param callback: Callback to check for. @rtype: boolean @return: True if the event-handler has been bound. """ if packetType in self.__callbacks: for callback_, customPacket in self.__callbacks[packetType]: if callback_ == callback: return True return False def raisePacketEvent(self, *packets): """Raise a packet event as if the packet was received through InSim. Useful for testing and debugging. @type packets: list @param packets: A list of packets to raise events for. """ for packet in packets: if packet.Type in self.__callbacks: for callback, customPacket in self.__callbacks[packet.Type]: callback(self, packet) class OutReceiver: """Class to handle incoming UDP packets from LFS, OutSim, OutGauge and IS_MCI/IS_NLP packets. """ def __init__(self, mode=OUT_INSIM, timeout=10.0, name='default'): """Initailise the OutReceiver object. The mode defines what sort of packets OutReceiver raises events for. >>> OUT_INSIM - InSim packets, such as MCI and NLP. >>> OUT_OUTSIM - OutSim packets. >>> OUT_OUTGAUGE - OutGauge packets. Upon timing out OutReceiver raises EVT_TIMEOUT event. @type mode: enum @param mode: The mode for the OutReceiver from the OUT_ enumeration. @type timeout: number @param timeout: The number of seconds to wait before timing out. @type name: string @param name: An optional name for the OutReceiver. """ self.__mode = mode self.__timeout = timeout self.__name = name self.__sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.__sock.settimeout(timeout) self.__thread = threading.Thread(target=self.__receiveThread) self.__callbacks = {} self.__connected = False def setTimeout(self, timeout): """Set the timeout seconds. @type timeout: number @param timeout: The timeout. """ self.__timeout = timeout def getTimeout(self): """Get the timeout seconds. @rtype: number @return: The timeout. """ return self.__timeout def getName(self): """Get the name of the socket. @rtype: string @return: The socket name. """ return self.__name def setName(self, name): """Set the name of the socket. @type name: string @param name: The socket name. """ self.__name = name def isConnected(self): """Get if the OutReceiver is connected. @rtype: boolean @return: True if connected. """ return self.__connected def start(self, host='localhost', port=30000): """Start the OutReceiver. @type host: string @param host: The host IP to receive data on. @type port: number @param port: The port to receive data through. """ self.__sock.bind((host, port)) self.__connected = True self.__thread.start() def stop(self): """Stop the OutReceiver. """ self.__stop() self.onOutEvent(EVT_CLOSE, CLOSE_REQUEST) def __stop(self): """Internal version of stop() that doesn't raise a close event. """ self.__sock.close() self.__connected = False def run(self): """Block the calling thread until the OutReceiver has closed. """ self.__thread.join() def bind(self, evtType, callback): """Bind an event-handler. @type evtType: enum @param evtType: Type of event. @type callback: function @param callback: Function to bind. """ if evtType not in self.__callbacks: self.__callbacks[evtType] = [] self.__callbacks[evtType].append(callback) def unbind(self, evtType, callback): """Unbind an event-handler. @type evtType: enum @param evtType: Type of event. @type callback: function @param callback: Function to unbind. """ self.__callbacks[evtType].remove(callback) def isBound(self, evtType, callback): """Get if an event-handler has been bound. @type evtType: enum @param evtType: Type of event. @type callback: function @param callback: Function to check. @rtype: boolean @return: True if the function has been bound. """ if evtType in self.__callbacks: return callback in self.__callbacks[evtType] return False def __receiveThread(self): """Internal UDP receive thread. """ try: while self.__connected: data = self.__sock.recv(_INSIM_BUFFER_SIZE) if data: if self.__mode == OUT_OUTGAUGE: packet = OutGaugePack() type_ = ISP_OUTGAUGE elif self.__mode == OUT_OUTSIM: packet = OutSimPack() type_ = ISP_OUTSIM else: type_ = _packetType(data) packet = _PACKET_DEFS[type_]() packet.unpack(data) if type_ in self.__callbacks: [callback(self, packet) for callback in self.__callbacks[type_]] else: self.onOutEvent(EVT_CLOSE, CLOSE_LFS) break except socket.timeout, err: self.onOutEvent(EVT_TIMEOUT, self.__timeout) except Exception, err: self.onOutEvent(EVT_ERROR, err) finally: self.__stop() def onOutEvent(self, evtType, args): """Virtual method called when a Closed event is raised. @type evtType: enum @param evtType: The type of the event from the EVT_ enumeration. @type args: args @param args: The close arguments. """ if evtType in self.__callbacks: [callback(self, args) for callback in self.__callbacks[evtType]] def raisePacketEvent(self, *packets): """Raise a packet-event as if it has been received by the OutReceiver. @type packets: list @param packets: A list of packets to raise events for. """ for packet in packets: if isinstance(packet, OutGaugePack): type_ = ISP_OUTGAUGE elif isinstance(packet, OutSimPack): type_ = ISP_OUTSIM else: type_ = packet.Type if type_ in self.__callbacks: [callback(self, packet) for callback in self.__callbacks[type_]] class OutSim(OutReceiver): """Motion Simulator Support. Simple wrapper for OutReceiver, which handles an OutSim socket. See OutReceiver for more info. """ def __init__(self, timeout=10.0, name='default'): """Initailise the OutSim object. @type timeout: number @param timeout: The number of seconds to wait before timing out. @type name: string @param name: An optional name for the OutSim socket. """ OutReceiver.__init__(self, OUT_OUTSIM, timeout, name) class OutGauge(OutReceiver): """External dashboard support. Simple wrapper for OutReceiver, which handles an OutGauge socket. See OutReceiver for more info. """ def __init__(self, timeout=10.0, name='default'): """Initailise the OutGauge object. @type timeout: number @param timeout: The number of seconds to wait before timing out. @type name: string @param name: An optional name for the OutGauge socket. """ OutReceiver.__init__(self, OUT_OUTGAUGE, timeout, name) if __name__ == '__main__': pass