#!/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