InSim System for Live for Speed : (Version : S2 ALPHA 0.5V3)
=================================

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.5Q
------------------
IS_RES packet : New field for hours (when total time >= 1 hour)
IS_NLP / IS_MCI : Highest 3 bits of node element used for more laps

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	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 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;		// NEW! 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];	// NEW! short car name
	char	SName	[16];	// NEW! skin name
	byte	Tyres	[4];	// NEW! tyre compounds (see below)
	byte	Spare	[8];

	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];	// NEW! short car name

	word	LapsDone;	// NEW! laps completed
	word	Flags;		// NEW! player flags
	byte	Penalty;	// NEW! current penalty value (see below)
	byte	NumStops;	// NEW! 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) ... NEW! ... if guest is V3 or later
{
	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 ... NEW! ... if guest is V3 or later
{
	char	PSF	[4];

	MSHT		Time;	// stop time
	unsigned	Spare;

	byte	PlyNum;
	byte	UniqueId;
	word	VerifyId;
};

struct IS_PLA // Pit LAne ... NEW! ... if guest is V3 or later
{
	char	PLA	[4];

	byte	Fact;		// pit lane fact (see below)
	byte	Sp1;
	byte	Sp2;
	byte	Sp3;

	byte	PlyNum;
	byte	UniqueId;
	word	VerifyId;
};

struct IS_PEN // PENalty (given or cleared) ... NEW!
{
	char	PEN	[4];

	byte	OldPen;		// old penalty value (see below)
	byte	NewPen;		// new penalty value (see below)
	byte	Sp2;
	byte	Sp3;

	byte	PlyNum;		// player's number
	byte	UniqueId;	// player's unique id
	word	VerifyId;
};

struct IS_TOC // Take Over Car ... NEW!
{
	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) ... NEW!
{
	char	FLG	[4];

	byte	OffOn;		// 0 = off / 1 = on
	byte	Flag;		// 1 = given blue / 2 = causing yellow
	byte	Sp2;
	byte	Sp3;

	byte	PlyNum;		// player's number
	byte	UniqueId;	// player's unique id
	word	VerifyId;
};

struct IS_PFL // Player FLags (help flags changed) ... NEW!
{
	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];	// NEW! 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

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

Player flags
------------
SWAPSIDE		1
GEARCHANGECUT		2
GEARCHANGEBLIP		4
AUTOGEARS		8

OLD MOUSE		32
HELP_BRAKE		64
HELP_THROTTLE		128

AUTOCLUTCH		512
MOUSE			1024
KB_NO_HELP		2048
KB_STABILISED		4096

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

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	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)


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 - 23 March 2007