InSim System for Live for Speed : (Version : S2 ALPHA 0.5P2) ================================= 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 S1 ================ IS_RES packet has been changed - same size but values added / moved RaceLaps variable now has new meanings - section below "Data types" Changes since 0.5P ================== FIX : InSim excessive packet flood on race restart FIX : Text commands can now be issued at a fast rate InSimVer - InSim version identifier increased to 2 (was 1) Various new text commands added - useful for some InSim programs 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 integer 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 Id [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 Id [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 Id [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 Driver 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 Id [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 Id [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 Id [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 Id [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 Id [4]; // MSS + 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 Id [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 Id [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 Id [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 Id [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's using 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) 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; byte Sp0; byte Sp1; 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 [32]; // car name word Flags; // player flags (see below) byte Type; // bit 0 - female / bit 1 - AI byte UniqueId; // player's 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 assigned 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 assigned 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 [32]; // car name MSHT Time; // lap time byte PlyNum; // player's number byte UniqueId; // player's assigned 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 assigned unique id word VerifyID; }; struct IS_RES // RESult (qualify or finish) { char RES [4]; char UName [24]; // username char PName [24]; // nickname char Plate [8]; // number plate - NO ZERO AT END! char CName [4]; // short car name / skin prefix (e.g. XRT) char Spare [24]; // 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; // position in results table (qualify may be inserted not 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 assigned 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; }; Player flags definition ----------------------- SWAPSIDE 1 GEARCHANGECUT 2 GEARCHANGEBLIP 4 AUTOGEARS 8 RESERVED_1 16 OLD MOUSE 32 HELP_BRAKE 64 HELP_THROTTLE 128 RESERVED_2 256 AUTOCLUTCH 512 MOUSE 1024 KB_NO_HELP 2048 KB_STABILISED 4096 Confirmation flags definition ----------------------------- 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. When ISF_NLP_MCI flag is NOT SET... struct NodeLap // NOT A PACKET - small 4-byte structure - part of the NLP (below) { word Node; // current path node byte Lap; // current lap byte UniqueId; // player's assigned 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... struct CompCar // NOT A PACKET - Car info in a 24-byte structure - part of the MCI (below) { word Node; // current path node byte Lap; // current lap byte UniqueId; // player's assigned 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 at the end of the README.txt 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 first 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 Id [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 Id [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) New text commands added to LFS - useful for InSim : =================================================== Note : most host / admin commands are now available in single player New command line startup options : ---------------------------------- /insim=PORT (PORT is between 1 and 65535) /windowed=X (X = no/yes - overrides the cfg.txt setting) Commands - general : -------------------- /spr and /mpr commands to run SPR or MPR from main entry screen /end command to exit from replay back to front end /qualify and /restart for host / single player / admin /sp - go into single player from entry screen /mp LOCAL_IP_ADDRESS PORT - join a local mp game from entry screen /spectate USERNAME - force username into spectate /pitlane USERNAME - send username to the pit lane /p_dt USERNAME - give drive through penalty /p_sg USERNAME - give stop-go penalty /p_30 USERNAME - give 30 second time penalty /p_45 USERNAME - give 45 second time penalty Race Control Messages : (big text in centre of screen) ----------------------- /rcm MESSAGE ... set a Race Control Message to be sent /rcm_ply USERNAME ... send the RCM to USERNAME /rcm_all ... send the RCM to all /rcc_ply USERNAME ... Clear USERNAME's RCM /rcc_all ... Clear all RCMs Game setup screen only : ------------------------ /ai [NAME] add ai driver (can specify NAME) /ready and /cancel - set ready or cancel ready /clear - clear all racers from list Game setup screen or in game : ------------------------------ /car XXX - select car (e.g. XRT) /setup X - select setup X /colour X - select colour X /join - join the race /spectate - spectate or leave grid /leave - disconnect from host /player X - select existing player X LFS - 28 July 2005