InSim System for Live for Speed : (Version : S2 ALPHA 0.5X) ================================= InSim allows communication between an external program and LFS.exe. UDP packets can be sent in either direction, LFS reporting various things about its state, and the external program requesting info and controlling LFS with special packets, text commands or keypresses. Changes since 0.5W ------------------ changed packets (size not changed) : IS_NPL : Handicap and passenger info added IS_PEN : Penalty reason byte added IS_FLG : Added unique id of obstructed player Player flags : Added SHIFTER, AXIS_CLUTCH, CUSTOM_VIEW new packets : IS_CCH (camera changed) InSimInfo (/i message sent by user to host's InSim) Changes since 0.5V ------------------ InSimVer : InSim version increased to 3 changed packets (size not changed) : IS_RST - Added race flags (MUST_PIT, etc) IS_NPL - CName now 4 chars + added skin name and tyre compounds IS_LAP - CName now 4 chars + added number of laps / flags / penalty / stops IS_RES - New FIN version of this is sent immediately on finishing race new packets : IS_PIT (pit stop) IS_PSF (pit stop finished) IS_PEN (penalty) IS_TOC (take over car) IS_FLG (yellow or blue flags) IS_PFL (player help flags) Data types : (all multi-byte types are PC style - lowest byte first) ============ char 1-byte character byte 1-byte unsigned integer word 2-byte unsigned integer short 2-byte signed integer unsigned 4-byte unsigned integer int 4-byte signed integerz float 4-byte float MSHT 4-byte minutes, seconds, hundredths, thousandths RaceLaps (rl) : (various meanings depending on range) =============== 0 : practice 1-99 : number of laps... laps = rl 100-190 : 100 to 1000 laps... laps = (rl - 100) * 10 + 100 191-238 : 1 to 48 hours... hours = rl - 190 InSim initialisation ==================== To initialise the InSim system, type into LFS : /insim XXXXX // XXXXX is the UDP port you want LFS to open OR start LFS with the command line option : LFS /insim=XXXXX This will make LFS listen for packets on that UDP port. Now you must send a special UDP packet to LFS. struct InSimInit // UDP packet to initialise the InSim system { char ISI [4]; // ISI + zero word Port; // Port for UDP replies from LFS (0...65535) byte Flags; // Bit flags for options - see below byte NodeSecs; // Number of seconds between NLP or MCI packets (0=none) char Admin [16]; // Admin password (required if set in LFS host options) }; Now you can send packets to control LFS, and LFS will be sending packets to you. Flags in InSimInit packet : set the relevant bits to turn on the option : #define ISF_RACE_TRACKING 1 // bit 0 : turns on race tracking #define ISF_GUARANTEE 2 // bit 1 : turns on guaranteed delivery #define ISF_SPLIT_MESSAGE 4 // bit 2 : use MSS, not MSO for user messages #define ISF_NO_WARNINGS 8 // bit 3 : turns off packet warnings #define ISF_KEEP_ALIVE 16 // bit 4 : makes lfs send keep alive packets #define ISF_NLP_MCI 32 // bit 5 : makes lfs send MCI instead of NLP Note : If Port is zero, LFS will send replies back to the port of the incoming packet InSimPack ========= InSimPack is used for various InSim requests (see below) struct InSimPack // General purpose 8 byte UDP packet { char Id [4]; // 3 character identifier followed by zero character int Value; // 32 bit value depending on the type of InSimPack }; Version Request =============== 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. This small version packet can be sent on request : struct InSimVersion { char VER [4]; // VER + zero char Version [8]; // LFS version, e.g. 0.3G char Product [6]; // Product : DEMO or S1 word InSimVer; // InSim Version : increased when InSim packets change }; To request an InSimVersion packet, send an InSimPack like this : Id : "VER" (VERsion) Value : 0 Closing Insim ============= You can send a packet to ask InSim to return to its waiting / initialised / listening state by sending an InSimPack with Id = "ISC" (InSimClose) and Value = 0. It will then require another InSimInit packet to start operating again. You can shut down InSim completely and stop it listening at all by typing in /insim=0 into the connected LFS. Or send a MsgTypePack to do the same thing. Maintaining the connection - Important ====================================== If InSim does not receive a packet for 2 minutes, it will close the connection and return to its original waiting / initialised state. To open it again you would need to send another InSimInit packet. To avoid InSim closing in this way, you should send a packet every 20 - 30 seconds. One example of a "keep alive" packet is an "ACK" InSimPack with value = 0. This "blank acknowledgment packet" will have no other effect on LFS. Note 1 : If you set the ISF_KEEP_ALIVE option in the InSimInit packet, then LFS will send a blank acknowledgement packet whenever it has not sent anything for 20 seconds. If you don't receive anything for 2 minutes it may be safe to assume that you have lost the connection for some reason. This option is also useful if your program doesn't run at all while it is waiting for a packet to arrive. Note 2 : If you want to request a small reply from LFS to check the connection at any time, a good one to send is a "GTH" InSimPack - LFS returns a "RTP" InSimPack. This has no functional effect on LFS as it is simply a time reporting packet (see below). State Reporting and Request =========================== LFS will send a StatePack any time the info in the StatePack changes. struct StatePack { char STA [4]; // STA + zero float ReplaySpeed; // 4-byte float - 1.0 is normal speed word Flags; // State Flags (see below) byte InGameCam; // Which type of camera is selected (see below) byte ViewPlayer; // Player index of viewed car byte NumPlayers; // Num in race byte NumConns; // Num connections including host byte NumFinished; // Number finished or qualified byte RaceInProgress; // 0 - no / 1 - race / 2 - qualifying byte QualMins; byte RaceLaps; // see "RaceLaps" near the top of this document byte Spare2; byte Spare3; char Track [6]; // short name for track e.g. FE2R byte Weather; // 0,1,2... byte Wind; // 0=off 1=weak 2=strong }; The bits stored in "State Flags" are : -------------------------------------- #define ISS_GAME 1 // in game (or MPR) #define ISS_REPLAY 2 // in SPR #define ISS_PAUSED 4 // paused #define ISS_SHIFTU 8 // in SHIFT+U mode #define ISS_SHIFTU_HIGH 16 // HIGH view #define ISS_SHIFTU_FOLLOW 32 // following car #define ISS_SHIFTU_NO_OPT 64 // buttons are hidden #define ISS_SHOW_2D 128 // showing 2d display #define ISS_FRONT_END 256 // in front end screen #define ISS_MULTI 512 // multiplayer mode #define ISS_MPSPEEDUP 1024 // multiplayer speedup option #define ISS_WINDOWED 2048 // lfs is running in a window #define ISS_SOUND_MUTE 4096 // sound is switched off #define ISS_VIEW_OVERRIDE 8192 // override user view InGameCam shows the NOT SHIFT+U selected camera mode ---------------------------------------------------- 0 Arcade View 1 Helicopter View 2 TV Camera View 3 Cockpit View 4 Custom View To request a StatePack from LFS, send an InSimPack like this : -------------------------------------------------------------- Id : "SST" (Send State) Value : 0 Setting states -------------- These states can be controlled by a special packet : ISS_SHIFTU_FOLLOW // following car ISS_SHIFTU_NO_OPT // buttons are hidden ISS_SHOW_2D // showing 2d display ISS_MPSPEEDUP // multiplayer speedup option ISS_SOUND_MUTE // sound is mute struct StateFlagsPack { char SFP [4]; // SFP + zero word Flag; // the state to set (ISS_xxx) byte OffOn; // 0 = off / 1 = on byte Sp3; // spare }; Other states must be set by using keypresses or messages (see below) Text Messages and Key Presses ============================= You can send 64-byte text messages to LFS as if the user had typed them in. Messages that appear on LFS screeen (up to 128 bytes) are reported to the external program. You can also send simulated keypresses to LFS. struct MsgTypePack // 64 chars - send to LFS to simulate typing message or command { char MST [4]; // MST + zero char Msg [64]; // text message or /command, must end with zero }; struct MsgToConn // 64 chars : send to LFS and on to a chosen connection (0 = host) { char MTC [4]; // MTC + zero byte Conn; // connection byte UniqueId; // destination player UniqueId : if set, Conn is ignored byte Sp2; byte Sp3; char Msg [64]; // text message or /command, must end with zero }; struct MsgOutPack // 128 chars : LFS reporting displayed messages { char MSO [4]; // MSO + zero char Msg [128]; // displayed message, with colours removed }; struct MsgOutSplit // 64 chars : user messages if ISF_SPLIT_MESSAGE flag is ON { char MSS [4]; // MSS + zero char UName [24]; // user name char PName [24]; // player name char Msg [64]; // text message from user }; struct InSimInfo // 64 chars : /i message from user to host's InSim ... NEW! { char III [4]; // III + zero char UName [24]; // user name char PName [24]; // player name char Msg [64]; // text message from user }; struct SingleCharPack // send to LFS to simulate single character { char SCH [4]; // SCH + zero char Char; // key to press byte Flags; // bit 0 : SHIFT / bit 1 : CTRL byte Spare2; byte Spare3; }; Note 1 : you must set the "Flags" bits if you want to simulate a key that needs CTRL or SHIFT held down. Note 2 : a new command has been added to LFS : /mso Typing "/mso MESSAGE" into LFS will send a MSO packet to the external program. Multiplayer Notification ======================== LFS will send this packet when a host is started or joined : struct InSimMulti { char ISM [4]; // ISM byte Host; // 0 = guest / 1 = host byte Sp1; byte Sp2; byte Sp3; char Name [32]; // the name of the host joined or started }; On ending or leaving a host, LFS will send this InSimPack : Id : "MPE" (MultiPlayerEnd) Value : 0 To request a InSimMulti packet at any time, send a InSimPack with Id = "ISM". - If LFS is not in multiplayer mode, the host name in the ISM will be empty. Vote Notify and Cancel ====================== LFS notifies the external program of any votes to restart or qualify The Vote Actions are defined as : VOTE_NONE 0 VOTE_END_RACE 1 VOTE_RESTART 2 VOTE_QUALIFY 3 struct InSimVote { char VTN [4]; // VTN + zero (VoTe Notify) byte Conn; // connection (0 is host) byte Type; // VOTE_X (Vote Action as defined above) byte Spare2; byte Spare3; }; When a vote is cancelled, LFS sends this InSimPack to the control program Id : "VTC" (VoTe Cancel) Value : 0 When a vote is completed, LFS sends this InSimPack to the control program Id : "VTA" (VoTe Action) Value : action (VOTE_X - Vote Action as defined above) The external program can instruct LFS host to cancel a vote using an InSimPack Id : "VTC" (VoTe Cancel) Value : 0 Setting Screen Mode =================== You can send this packet to LFS to set the screen mode : struct InSimMode { char MOD [4]; // MOD + zero (MODe) int Bits16; // set to choose 16-bit mode int RR; // refresh rate - zero for default int Width; // 0 - go to window int Height; // 0 - go to window }; The refresh rate actually selected by LFS will be the highest available rate that is less than or equal to the specified refresh rate. Refresh rate can be specified as zero in which case the default refresh rate will be used. If Width and Height are both zero, LFS will switch to windowed mode. Race Tracking ============= Special Packets are provided for Race Tracking. These will be sent if InSim was initialised with the ISF_RACE_TRACKING flag set. Because it uses UDP protocol, it's possible some packets may be lost. So a simple guaranteed delivery system has been implemented : You must send an aknowledgment reply to LFS after receiving these packets, or LFS will try to send them again. An acknowledgment packet is an InSimPack like this : Id : "ACK" (ACKnowledge) Value : VerifyId (as given in the received packet) NOTE about Race Tracking ------------------------ In LFS there are is a list of connections AND a list of players in the race. If you are making a multiplayer IsSim program, you must maintain two lists. You should use UName as a unique identifier for the connection, or if you are making a Demo or LAN version, you can use PName. PName can change while a player is connected (tracked in the IS_CPR packet) so it's not as good as UName, which is guaranteed not to change. Each player has a unique identifier from the moment he joins the race, until he leaves or spectates. It's not possible for a player identifier and a connection identifier to be the same thing, for two reasons (1) there may be more than one player per connection if AI drivers are used (2) a player can swap between connections, in the case of a driver swap (tracked in the IS_TOC packet), RACE TRACKING PACKETS - you must send an acknowledgement reply to these packets --------------------- struct IS_RST // Race STart { char RST [4]; byte RaceLaps; // see "RaceLaps" near the top of this document byte QualMins; // 0 if race byte NumInRace; byte Spare; char Track [6]; byte Weather; byte Wind; word Flags; // race flags (see below) word VerifyId; }; struct IS_REN // Race ENd (return to entry screen) { char REN [4]; byte Sp0; byte Sp1; word VerifyId; }; struct IS_NCN // New ConN { char NCN [4]; char UName [24]; // username char PName [24]; // nickname byte Admin; byte Sp1; byte Sp2; byte Sp3; byte ConnNum; // new conn's number (0 = host, 1, 2...) byte Total; // number of connections on host, including host word VerifyId; }; struct IS_CNL // ConN Leave (end connection is moved down into this slot) { char CNL [4]; char UName [24]; // username char PName [24]; // nickname byte ConnNum; byte Total; word VerifyId; }; struct IS_NPL // New PLayer joining race (if number already exists, then leaving pits) { char NPL [4]; char UName [24]; // username char PName [24]; // nickname char Plate [8]; // number plate - NO ZERO AT END! char CName [4]; // short car name char SName [16]; // skin name byte Tyres [4]; // tyre compounds (see below) byte H_Mass; // NEW! handicap : added mass (kg) byte H_TRes; // NEW! handicap : intake restriction byte SpareByte; // byte Passengers; // NEW! passengers byte (see below) unsigned Spare; word Flags; // player flags (see below) byte Type; // bit 0 - female / bit 1 - AI byte UniqueId; // player's newly assigned unique id byte PlyNum; // player's number (0 = pole, 1, 2...) byte Total; // number in race (same when leaving pits, 1 more if new) word VerifyId; }; struct IS_PLP // PLayer Pits (go to settings - stays in player list) { char PLP [4]; char UName [24]; // username char PName [24]; // nickname byte UniqueId; // player's unique id byte Sp1; byte Sp2; byte Sp3; byte PlyNum; // player's number (stays in list) byte Total; // new total (expect : same as before) word VerifyId; }; struct IS_PLL // PLayer Leave race (spectate - leaves player list, all are shunted down) { char PLL [4]; char UName [24]; // username char PName [24]; // nickname byte UniqueId; // player's unique id byte Sp1; byte Sp2; byte Sp3; byte PlyNum; // player's number (others shunt down) byte Total; // new total (expect : 1 less than before) word VerifyId; }; struct IS_CPR // Conn Player Rename { char CPR [4]; char UName [24]; // username char OldName [24]; // old name char NewName [24]; // new name char Plate [8]; // number plate - NO ZERO AT END! byte UniqueId; // unique id : 0 = connection has no player in race byte Sp1; word VerifyID; }; struct IS_CLR // CLear Race - all players removed from race in one go { char CLR [4]; byte Sp0; byte Sp1; word VerifyId; }; struct IS_LAP // LAP time { char LAP [4]; char UName [24]; // username char PName [24]; // nickname char CName [4]; // short car name word LapsDone; // laps completed word Flags; // player flags byte Penalty; // current penalty value (see below) byte NumStops; // number of pit stops byte Sp2; byte Sp3; byte SpareB [20]; MSHT Time; // lap time byte PlyNum; // player's number byte UniqueId; // player's unique id word VerifyId; }; struct IS_SPX // SPlit X time { char SPX [4]; // SP1 / SP2 / SP3 + zero MSHT Time; // split time byte PlyNum; // player's number byte UniqueId; // player's unique id word VerifyID; }; struct IS_PIT // PIT stop (stop at pit garage) { char PIT [4]; word LapsDone; // laps completed word Flags; // player flags byte Penalty; // current penalty value (see below) byte NumStops; // number of pit stops byte Sp2; byte Sp3; byte Tyres [4]; // tyres changed unsigned Work; // pit work (see below) unsigned Spare; byte PlyNum; // player's number byte UniqueId; // player's unique id word VerifyId; }; struct IS_PSF // Pit Stop Finished { char PSF [4]; MSHT Time; // stop time unsigned Spare; byte PlyNum; byte UniqueId; word VerifyId; }; struct IS_PLA // Pit LAne { char PLA [4]; byte Fact; // pit lane fact (see below) byte Sp1; byte Sp2; byte Sp3; byte PlyNum; byte UniqueId; word VerifyId; }; // NOTE : To track cameras you need to consider 3 points // 1) The default camera : VIEW_DRIVER // 2) Player flags : CUSTOM_VIEW means VIEW_CUSTOM at start or pit exit // 3) IS_CCH : sent when an existing driver changes camera struct IS_CCH // Camere CHange ... NEW! { char CCH [4]; byte Camera; // view identifier (see below) byte Sp1; byte Sp2; byte Sp3; byte PlyNum; byte UniqueId; word VerifyId; }; struct IS_PEN // PENalty (given or cleared) { char PEN [4]; byte OldPen; // old penalty value (see below) byte NewPen; // new penalty value (see below) byte Reason; // NEW! penalty reason (see below) byte Sp3; byte PlyNum; // player's number byte UniqueId; // player's unique id word VerifyId; }; struct IS_TOC // Take Over Car { char TOC [4]; char OldUName[24]; // old username char OldPName[24]; // old nickname char NewUName[24]; // new username char NewPName[24]; // new username byte PlyNum; // player's number byte UniqueId; // player's unique id (stays with this car) word VerifyId; }; struct IS_FLG // FLaG (yellow or blue flag changed) { char FLG [4]; byte OffOn; // 0 = off / 1 = on byte Flag; // 1 = given blue / 2 = causing yellow byte CarBehind; // unique id of obstructed player byte Sp3; byte PlyNum; // player's number byte UniqueId; // player's unique id word VerifyId; }; struct IS_PFL // Player FLags (help flags changed) { char PFL [4]; word Flags; // player flags (see below) word Spare; byte PlyNum; // player's number byte UniqueId; // player's unique id word VerifyId; }; // NOTE 1 : The IS_RES packet is used for two purposes at the end of a race // 1) FINished - sent immediately on finish, often in the wrong order // 2) RESult - sent a while later, when the final position is confirmed // NOTE 2 : ResultNum may be 255 in two situations // 1) When the IS_RES is a FINished packet ResultNum has no meaning // 2) During qualifying, when the result is not added to the table struct IS_RES // FINished or RESult (qualify or finish) { char FIN_RES [4]; // FIN : finished race / RES : qualify or result char UName [24]; // username char PName [24]; // nickname char Plate [8]; // number plate - NO ZERO AT END! char CName [4]; // short car name char Spare [20]; // zero word Hours; // total hours (when TotalTime >= 1 hour) word SpareW; // zero word LapsDone; // laps completed word Flags; // player flags : help settings etc - see below byte ConfirmFlags; // confirmation flags : disqualified etc - see below byte NumStops; // number of pit stops byte Type; // bit 0 - female / bit 1 - AI byte UniqueId; // unique id : 0 = player already left before result was sent MSHT TotalTime; // race time MSHT BestLap; // best lap byte ResultNum; // finish position or 255 (qualify might not be inserted at end) byte NumResults; // total number of results (qualify doesn't always add a new one) word VerifyId; }; struct PosId // NOT A PACKET - small 2-byte structure - part of the REO (below) { byte OldPos; // old position in list byte UniqueId; // player's unique id }; struct IS_REO // REOrder (when race restarts after qualifying) { char REO [4]; PosId Info [28]; // old positions and unique id byte Sp0; // spare byte NumPlayers; // number of players in race word VerifyId; }; Race flags ---------- HOSTF_CAN_VOTE 1 HOSTF_CAN_SELECT 2 HOSTF_MID_RACE 32 HOSTF_MUST_PIT 64 HOSTF_CAN_RESET 128 Pit work flags -------------- PSE_NOTHING 1 PSE_STOP 2 PSE_FR_DAM 4 PSE_FR_WHL 8 PSE_LE_FR_DAM etc... PSE_LE_FR_WHL PSE_RI_FR_DAM PSE_RI_FR_WHL PSE_RE_DAM PSE_RE_WHL PSE_LE_RE_DAM PSE_LE_RE_WHL PSE_RI_RE_DAM PSE_RI_RE_WHL PSE_BODY_MINOR PSE_BODY_MAJOR PSE_SETUP PSE_REFUEL Pit lane facts -------------- PITLANE_EXIT 0 // left pit lane PITLANE_ENTER 1 // entered pit lane PITLANE_NO_PURPOSE 2 // entered for no purpose PITLANE_DT 3 // entered for drive-through PITLANE_SG 4 // entered for stop-go View identfiers --------------- VIEW_FOLLOW 0 // arcade VIEW_HELI 1 // helicopter VIEW_CAM 2 // tv camera VIEW_DRIVER 3 // cockpit VIEW_CUSTOM 4 // custom VIEW_ANOTHER 255 // viewing another car Penalty values (VERIFIED means the penalty can now be cleared) -------------- PENALTY_NONE 0 PENALTY_DT 1 PENALTY_DT_VERIFIED 2 PENALTY_SG 3 PENALTY_SG_VERIFIED 4 PENALTY_30 5 PENALTY_45 6 Penalty reasons --------------- PENR_UNKNOWN 0 // unknown or cleared penalty PENR_ADMIN 1 // penalty given by admin PENR_WRONG_WAY 2 // wrong way driving PENR_FALSE_START 3 // starting before green light PENR_SPEEDING 4 // speeding in pit lane PENR_STOP_TOO_SHORT 5 // stop-go pit stop too short PENR_STOP_TOO_LATE 6 // compulsory stop is too late Player flags ------------ SWAPSIDE 1 GEARCHANGECUT 2 GEARCHANGEBLIP 4 AUTOGEARS 8 SHIFTER 16 RESERVED_1 32 HELP_BRAKE 64 AXIS_CLUTCH 128 RESERVED_2 256 AUTOCLUTCH 512 MOUSE 1024 KB_NO_HELP 2048 KB_STABILISED 4096 CUSTOM_VIEW 8192 // custom view is selected at start Tyre compounds (4 byte order : rear L, rear R, front L, front R) -------------- 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 NOT_CHANGED 255 Passengers byte --------------- bit 0 female bit 1 front bit 2 female bit 3 rear left bit 4 female bit 5 rear middle bit 6 female bit 7 rear right Confirmation flags ------------------ MENTIONED 1 CONFIRMED 2 PENALTY_DT 4 PENALTY_SG 8 PENALTY_30 16 PENALTY_45 32 DID_NOT_PIT 64 NON-VERIFIED TRACKING PACKETS - do not send an acknowledgement reply to these packets ----------------------------- If NodeSecs was set in the InSimInit packet, LFS will send IS_NLP or IS_MCI packets at the specified interval, depending on the InSimInit initialisation flag ISF_NLP_MCI. Note : NODE/LAP - To allow laps up to 2047 to be reported, without increasing the size of NLP and MCI packets, the next 3 high bits of the current lap are now placed in the highest 3 bits of the Node_Lap_Hi3 member (of NodeLap and CompCar structures). Example C code to extract the correct values from the packets : int node = Node_Lap_Hi3 & 0x1fff; int lap = Lap_Lo8 | ((Node_Lap_Hi3 & 0xe000) >> 5); When ISF_NLP_MCI flag is NOT SET, this packet is sent... struct NodeLap // NOT A PACKET - small 4-byte structure - part of the NLP (below) { word Node_Lap_Hi3; // current path node (and high 3 bits of Lap) byte Lap_Lo8; // current lap (low byte... see above : NODE/LAP) byte UniqueId; // player's unique id }; struct IS_NLP // Node and Lap Packet { char NLP [4]; word NumNodes; // total number of nodes in the path word FinishLine; // the node number of the finish line byte NumPlayers; // number of players in race byte Sp1; // spare byte Sp2; // spare byte Sp3; // spare NodeLap Info [28]; // node and lap of each player }; When ISF_NLP_MCI flag is SET, this packet is sent... struct CompCar // NOT A PACKET - Car info in a 24-byte structure - part of the MCI (below) { word Node_Lap_Hi3; // current path node (and high 3 bits of Lap) byte Lap_Lo8; // current lap (low byte... see above : NODE/LAP) byte UniqueId; // player's unique id int X; // X map (65536 = 1 metre) int Y; // Y map (65536 = 1 metre) int Z; // Z alt (65536 = 1 metre) word Speed; // speed (32768 = 100 m/s) word Direction; // direction of car's motion : (see note 1 below) word Heading; // direction of forward axis : (see note 1 below) short AngVel; // signed, rate of change of heading : (see note 2) }; Note 1 : 0 = world y axis direction, 32768 = 180 degrees, anticlockwise from above Note 2 : 0 = no change in heading, 8192 = 180 degrees per second anticlockwise struct IS_MCI // MultiCarInfo - if more than 8 in race then more than one of these is sent { char MCI [4]; word NumNodes; // total number of nodes in the path word FinishLine; // the node number of the finish line byte NumPlayers; // number of players in race byte FirstPly; // first player in this packet (0 in 1st packet, 8 in 2nd...) byte Sp2; // spare byte Sp3; // spare CompCar Info [8]; // car info for each player, max 8 per packet }; You can change the rate of NLP or MCI after initialisation by sending this InSimPack : Id : "NLI" (Node Lap Interval) Value : Interval (0 means stop, otherwise interval in milliseconds, minimum 100) TRACKING PACKET REQUESTS ------------------------ - send to LFS to request player / connection / result packets - these requests work even if race tracking is not switched on To request a IS_RES, send a InSimPack with Id = "RES" and value = result number (0,1,2...) To request a IS_NPL, send a InSimPack with Id = "NPL" and value = player number (0,1,2...) To request a IS_NCN, send a InSimPack with Id = "NCN" and value = conn number (0,1,2...) To request a IS_NLP, send a InSimPack with Id = "NLP" and value = 0 To request a IS_MCI, send a InSimPack with Id = "MCI" and value = 0 Car Position Packets : (Initialising OutSim from InSim) ====================== To request Car Positions from the currently viewed car, send this InSimPack : Id : "SSP" - Start Sending Positions (sends UDP packets using OutSim system) Value : delay between updates (zero means stop sending) The SSP packet makes LFS start sending UDP packets if in game, using the OutSim system as documented near the end of this text file. You do not need to set any OutSim values in LFS cfg.txt as OutSim is automatically initialised by the SSP packet. The OutSim packets will be sent to the UDP port specified in the InSimInit packet. Note : OutSim packets don't have a 4-byte header like InSim packets. - You can identify an OutSim packet by its size (64 bytes) and 4th byte : zero. Dashboard Packets : (Initialising OutGauge from InSim) =================== To request Dashboard Packets from the currently viewed car, send this InSimPack : Id : "SSG" - Start Sending Gauges (sends UDP packets using OutGauge system) Value : delay between updates (zero means stop sending) The SSG packet makes LFS start sending UDP packets if in game, using the OutGauge system as documented near the end of this text file. You do not need to set any OutGauge values in LFS cfg.txt as OutSim is automatically initialised by the SSG packet. The OutGauge packets will be sent to the UDP port specified in the InSimInit packet. Note : OutGauge packets don't have a 4-byte header like InSim packets. - You can identify an OutGauge packet by its size (92 bytes) and 4th byte : zero. Camera Control : ================ In-game mode camera control --------------------------- You can set the viewed car and selected camera directly with a special packet. These are the states normally set in-game by using the TAB and V keys. struct SetCarCamera // Simplified camera packet (not SHIFT+U mode) { char SCC [4]; // SCC + zero byte ViewPlayer; // Player Index of car to view (0 = pole...) byte InGameCam; // InGameCam (as reported in StatePack) byte UniqueId; // Overrides ViewPlayer if set byte Spare3; }; Special case : set ViewPlayer or InGameCam to 255 to leave that option unchanged. Note : if UniqueId is used, the ViewPlayer byte will be ignored Direct camera control --------------------- A Camera Position Packet can be used for LFS to report a camera position and state, and for the external program to set LFS camera position in game or SHIFT+U mode. Type : "Vec" : 3 ints (X, Y, Z) - 65536 means 1 metre struct CamPosPack // Full camera packet (in game OR SHIFT+U mode) { char CPP [4]; // CPP + zero Vec Pos; // Position vector word Heading; // 0 points along Y axis word Pitch; // 0 means looking at horizon word Roll; // 0 means no roll byte ViewPlayer; // Player Index of car to view (0 = pole...) byte InGameCam; // InGameCam (as reported in StatePack) float FOV; // 4-byte float : FOV in radians word Time; // Time to get there (0 means instant + reset) word Flags; // State Flags } State flags controlling this packet ----------------------------------- The relevant Flags that can be set are : #define ISS_SHIFTU 8 // in SHIFT+U mode #define ISS_SHIFTU_HIGH 16 // HIGH view #define ISS_SHIFTU_FOLLOW 32 // following car #define ISS_VIEW_OVERRIDE 8192 // override user view On receiving this packet, LFS will set up the camera to match the values in the packet, including switching into or out of SHIFT+U mode depending on the ISS_SHIFTU flag. If ISS_SHIFTU is not set, then ViewPlayer and InGameCam will be used. If ISS_VIEW_OVERRIDE is set, the in-car view Heading Pitch and Roll will be taken from the values in this packet. Otherwise normal in-game control will be used. Position Vector (Vec Pos) ------------------------- This is ignored for in-game (not SHIFT+U) cameras. For SHIFT+U cameras, Pos can be either relative or absolute. If ISS_SHIFTU_FOLLOW is set, that means it's a following camera, so the position is relative to the selected car. Otherwise, the position is an absolute camera position, as used in normal SHIFT+U mode. Smooth Camera Positioning ------------------------- The "Time" value in the packet is used for camera smoothing. A zero Time means instant positioning. Any other value (milliseconds) will cause the camera to move smoothly to the requested position in that time. This is most useful in SHIFT+U camera modes or for smooth changes of internal view when using the ISS_VIEW_OVERRIDE flag. Note : you can use frequently updated camera positions with a longer "Time" than your updates. For example, sending a camera position every 100 ms, with a Time value of 1000 ms. LFS will make a smooth motion from these "rough" inputs. If the requested camera mode is different from the one LFS is already in, it cannot move smoothly to the new position, so in this case the "Time" value is ignored. Getting a camera packet ----------------------- To GET a CamPosPack from LFS, send an InSimPack like this : Id : "SCP" (Send Camera Pos) Value : 0 LFS will reply with a CamPosPack as described above. You can store this packet and later send back exactly the same packet to LFS and it will try to replicate that camera position. Time Control : ============== You can Stop or Start Time in LFS and while it is stopped you can make LFS move in time steps in multiples of 100th of a second. Warning : unlike pausing, this is a "trick" to LFS and the program is unaware of time passing, so you must not leave it stopped because LFS is unusable in that state. You must never use this packet in multiplayer mode. Stop and Start with an InSimPack like this : Id : "TMS" (Time Stop) Value : 1 - stop / 0 - carry on When STOPPED, make time step updates with this InSimPack : Id : "STP" (Time Step) Value : 1, 2, 3, 4 etc, number of hundredths of a second to update Request the current time at any point with this InSimPack : Id : "GTH" (Get Time in Hundredths) Value : 0 The time will be sent back in this InSimPack : Id : "RTP" (Race Time Packet) Value : Time (Hundredths of a second since start of race or replay) OutSim - 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 Each update sends the following UDP packet : unsigned int :time in milliseconds (to check order) Angular Velocity :3 floats Orientation :3 floats Heading, Pitch, Roll Acceleration :3 floats X, Y, Z Velocity :3 floats X, Y, Z Position :3 ints X, Y, Z (metres x 65536) Game ID :1 int (optional ID - if specified in cfg.txt) Note 1 : X and Y axes are on the ground, Z is up. Note 2 : Motion simulators can be dangerous. The developers of the Live for Speed racing simulator do not support any motion systems in particular and cannot accept responsibility for any injuries or death connected with the use of such machinery. OutGauge - 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 Each update sends the following UDP packet : unsigned int Time; // time in milliseconds (to check order) char Car[4]; // car name word Flags; // OG_FLAGS, see below byte Gear; // Reverse:0, Neutral:1, First:2... byte SpareB; float Speed; // M/S float RPM; // RPM float Turbo; // BAR float EngTemp; // C float Fuel; // 0 to 1 float OilPress; // BAR float Spare1; float Spare2; float Spare3; float Throttle; // 0 to 1 float Brake; // 0 to 1 float Clutch; // 0 to 1 char Display1[16]; // usually fuel char Display2[16]; // usually settings int ID; // (optional identifier) #define OG_SHIFTLIGHT 1 #define OG_FULLBEAM 2 #define OG_HANDBRAKE 4 #define OG_PITSPEED 8 #define OG_TC 16 #define OG_HEADLIGHTS 32 #define OG_SIGNAL_L 64 #define OG_SIGNAL_R 128 #define OG_REDLINE 256 #define OG_OILWARN 512 #define OG_1 1024 #define OG_2 2048 #define OG_3 4096 #define OG_4 8192 #define OG_KM 16384 #define OG_BAR 32768 ---------------- LFS - April 2007