Latest commit

History

634 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Node InSim

NPM VersionNode.js CI

An InSim library for Node.js with TypeScript support.

Introduction

Node InSim provides a JavaScript API to communicate with the Live for Speed InSim protocol over a TCP connection. After connecting to an LFS host via a hostname and a port, you are able to send InSim packets to the host and receive incoming packets from the host.

All packet structures in Node InSim are identical to the structs defined in the InSim protocol. All packet classes with all their properties are documented according to the specification.

InSim compatibility

Node InSim is compatible with InSim version 10.

Installation

Install the node-insim NPM package in your Node.js application:

npm install --save node-insim

or if you use Yarn:

yarn add node-insim

Documentation

For more detailed documentation of the public API, see https://simbroadcasts.github.io/node-insim/.

Usage

Connecting

To connect to an LFS host, you must enter its hostname, a port and a short name of your InSim application.

The InSim port must be configured in the LFS host settings. Also, make sure the public IP address from which your application is connecting is allowed to connect to the host's InSim port.

Single host

import{InSim}from'node-insim';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});

Multiple hosts

To connect to multiple hosts at once, create a new InSim instance for each host.

import{InSim}from'node-insim';constinSim1=newInSim();inSim1.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constinSim2=newInSim();inSim2.connect({Host: '127.0.0.2',Port: 30000,IName: 'Node InSim App',});

TCP / UDP

By default, Node InSim opens a TCP connection. If you want to use UDP, set the Protocol option to UDP in the connect function.

import{InSim}from'node-insim';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',Protocol: 'UDP',});

Sending packets

InSim packets can be sent using the send() method on the InSim class instance, which takes a single argument - the packet class instance.

A fast way to set packet properties is to populate them in the class constructor:

import{InSim}from'node-insim';import{IS_TINY,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.send(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_PING,}),);

Another way is to assign each property after creating the instance:

import{InSim}from'node-insim';import{IS_TINY,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constpingPacket=newIS_TINY();pingPacket.ReqI=1;pingPacket.SubT=TinyType.TINY_PING;inSim.send(pingPacket);

Waiting for packets

There are cases when you want to send a packet and then wait for a response in another packet. There is a helper method sendAwait() which waits for a given packet type and when it's received, it's resolved as a Promise. It also makes sure that the received packet's ReqI property matches the one entered in the sent packet.

import{InSim}from'node-insim';import{IS_TINY,PacketType,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.sendAwait(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_SST,}),PacketType.ISP_STA,).then((packet)=>{console.log(packet.NumConns);});

You can filter the received packet by its data using a callback in the 3rd argument:

import{InSim}from'node-insim';import{IS_TINY,PacketType,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.sendAwait(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_GTP,}),PacketType.ISP_SMALL,({ SubT })=>SubT===SmallType.SMALL_RTP,).then((packet)=>{console.log('session time',packet.UVal);});

Request-reply packet pairs

NameRequest packetReply packet
PingIS_TINY
SubT: TinyType.TINY_PING
IS_TINY
SubT: TinyType.TINY_REPLY
Session timeIS_TINY
SubT: TinyType.TINY_GTP
IS_SMALL
SubT: SmallType.SMALL_RTP
StateIS_TINY
SubT: TinyType.TINY_SST
IS_STA
InSim multiTINY_ISMIS_ISM

Sending messages

The InSim class has helper methods useful for sending messages to LFS.

Send a message which will appear on the local computer only

inSim.sendLocalMessage('Local message');

Send a command

inSim.sendMessage('/end');

Send a message

  • up to 63 characters - send an IS_MST packet
  • 64 characters or more - send an IS_MSX packet
inSim.sendMessage('This is a message');

Send a message to a specific connection by their UCID

inSim.sendMessageToConnection(4,'This is a message targeting UCID 4');

Send a message to a specific player by their PLID

inSim.sendMessageToPlayer(4,'This is a message targeting PLID 4');

Receiving packets

The InSim class exposes an on() method, which is used to listen for incoming packets by their type.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>){console.log(`Connected to LFS ${packet.Product}${packet.Version}`);}

The event callback contains the received packet, and an optional second argument - the InSim instance which received that packet. You can use that instance to send additional packets in response.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType,TinyType}from'node-insim/packets';importtype{IS_TINY}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>,inSim: InSim,){inSim.send(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_PING,}),);}

Multiple hosts

You can use the inSim argument in the event handler callback to identify the source host of the received packets, for instance by the options.Host property.

Alternatively, the InSim class constructor accepts an optional id argument, which can also be used to tell apart the InSim connections.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType}from'node-insim/packets';constinSim1=newInSim('Host One');inSim1.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constinSim2=newInSim('Host Two');inSim2.connect({Host: '127.0.0.2',Port: 30000,IName: 'Node InSim App',});inSim1.on(PacketType.ISP_VER,onVersion);inSim2.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>,inSim: InSim,){console.log(`Connected to ${inSim.options.Host}:${inSim.options.Port}`);if(inSim.id){console.log(`InSim connection ID: ${inSim.id}`);}}

String encoding

All strings in received or sent packets are automatically converted from LFS encoding to Unicode and vice versa.

If you need to access the raw LFS-encoded string in a received packet, use the _raw property in the packet instance, which contains all unconverted string properties.

import{InSim}from'node-insim';import{PacketType}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_ISM,(packet)=>{console.log(packet.HName);// UTF-8 string - ^1Drifter Team ^7★ Serverconsole.log(packet._raw.HName);// raw string - ^1Drifter Team ^7^J�� Server\u0000\u0000\u0000\u0000});

When you send a Unicode string value in a packet, each character will get encoded into the correct LFS encoding, so LFS can display the text in a message or a button.

import{InSim}from'node-insim';import{PacketType}from'node-insim/packets';importtype{IS_MSL}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,(packet)=>{inSim.send(newIS_MSL({Msg: 'čau světe',// LFS will receive: ^Eèau svìte}),);});

Special characters

Special care needs to be taken when sending strings containing caret (^) and slash (/) characters:

  • A caret needs to be escaped as ^^ because the caret symbol acts as an escape character in LFS. Sending ^^hello as a message will display ^hello in LFS. Sending just ^hello would appear as #ello.
  • A slash needs to be escaped as ^s, otherwise it is treated as an LFS in-game command prefix when used in a message packet. Sending ^sjoin as a message will display /join in LFS, whereas sending /join will make the player join the track.

OutGauge

import{OutGauge}from'node-insim';constoutGauge=newOutGauge();outGauge.connect({Host: '127.0.0.1',Port: 29999,});outGauge.on('packet',(data)=>{console.clear();console.log(data.RPM);});

OutSim

import{OutSim,OutSimPack}from'node-insim';constoutSim=newOutSim();outSim.connect({Host: '127.0.0.1',Port: 29999,});outSim.on('packet',(data)=>{// Make sure the simple OutSimPack packet is really received, as opposed to OutSimPack2if(!(datainstanceofOutSimPack)){return;}console.clear();console.log(data.PosX);});

Debugging

Node InSim uses the debug NPM package for debug logs. By default, Node InSim does not output any logs to the standard output.

To enable logging, use the DEBUG environment variable when running your InSim application. All logs are prefixed with node-insim. You can use wildcards to filter out the logs that you need.

DEBUG=* node insim.js # debug all messages
DEBUG=node-insim:tcp node insim.js # debug only TCP protocol messages

Example applications

You can find example applications using Node InSim in the examples folder.

Example
InSim connectionJavaScript + CJSTypeScript + ESM
InSim connection (multiple hosts)JavaScript + CJSTypeScript + ESM
InSim connection (UDP)JavaScript + CJSTypeScript + ESM
OutGaugeJavaScript + CJSTypeScript + ESM
OutGauge with InSim buttonsJavaScript + CJSTypeScript + ESM
OutSimJavaScript + CJSTypeScript + ESM
OutSim with OptionsJavaScript + CJSTypeScript + ESM

Before you run an example, follow the instructions in each example's README.md file.

For instance, to run the "InSim connection - TypeScript" example, run the following commands:

cd examples/typescript/insim-connection
npm install
npm start

Development

Requirements

  • Node.js 18
  • Yarn

Start a development server

yarn dev

Code generators

When adding new InSim packets to the library, you can use built-in code generators using yarn generate. It will create and update all the necessary files for you.

Run unit tests

yarn test

Run tests against a real LFS application

To run these tests, LFS must be running with an InSim port open.

By default, the tests connect to 127.0.0.1:29999. The InSim host and port can be configured by copying .env to .env.local in the lfs-test directory.

yarn test:lfs

Build all example applications

This command will go through each application in the examples/ folder, install its dependencies, then build the application (typescript only).

yarn test:examples

Lint code

yarn lint

Format code

yarn format

Production build

Compiled files will be created in dist/.

yarn build

Run all checks at once

You can run code format, lint + fix, build and test with the following command:

yarn check-all

Releases

This project uses Changesets to manage publishing and release notes.

Making changes

Before committing a bug fix, a new feature or a breaking change, run the following command to create a changeset:

yarn changeset
  1. When prompted, choose if it's a patch, minor or a major change.
  2. Enter a summary for the change which will appear in the changelogs.
  3. When confirmed, a changeset Markdown file will be created in the .changeset directory.
  4. Commit the new changeset file along with your source code changes.

Releasing a new version

To bump the package version:

  1. Go to the GitHub Actions page of this repo.
  2. Select the Release workflow.
  3. Click "Run workflow".

This will bump Node InSim version according to the types of changes in the changesets and create a pull request. You can review the changes and merge the pull request once it's ready.

Once the version has been bumped, trigger the Release workflow again, which will publish a new version to NPM.


Node Insim - An open source project by Sim Broadcasts

About

A Node.js library for Live For Speed InSim protocol

Topics

Resources

Stars

16 stars

Watchers

3 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

634 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Node InSim

NPM VersionNode.js CI

An InSim library for Node.js with TypeScript support.

Introduction

Node InSim provides a JavaScript API to communicate with the Live for Speed InSim protocol over a TCP connection. After connecting to an LFS host via a hostname and a port, you are able to send InSim packets to the host and receive incoming packets from the host.

All packet structures in Node InSim are identical to the structs defined in the InSim protocol. All packet classes with all their properties are documented according to the specification.

InSim compatibility

Node InSim is compatible with InSim version 10.

Installation

Install the node-insim NPM package in your Node.js application:

npm install --save node-insim

or if you use Yarn:

yarn add node-insim

Documentation

For more detailed documentation of the public API, see https://simbroadcasts.github.io/node-insim/.

Usage

Connecting

To connect to an LFS host, you must enter its hostname, a port and a short name of your InSim application.

The InSim port must be configured in the LFS host settings. Also, make sure the public IP address from which your application is connecting is allowed to connect to the host's InSim port.

Single host

import{InSim}from'node-insim';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});

Multiple hosts

To connect to multiple hosts at once, create a new InSim instance for each host.

import{InSim}from'node-insim';constinSim1=newInSim();inSim1.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constinSim2=newInSim();inSim2.connect({Host: '127.0.0.2',Port: 30000,IName: 'Node InSim App',});

TCP / UDP

By default, Node InSim opens a TCP connection. If you want to use UDP, set the Protocol option to UDP in the connect function.

import{InSim}from'node-insim';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',Protocol: 'UDP',});

Sending packets

InSim packets can be sent using the send() method on the InSim class instance, which takes a single argument - the packet class instance.

A fast way to set packet properties is to populate them in the class constructor:

import{InSim}from'node-insim';import{IS_TINY,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.send(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_PING,}),);

Another way is to assign each property after creating the instance:

import{InSim}from'node-insim';import{IS_TINY,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constpingPacket=newIS_TINY();pingPacket.ReqI=1;pingPacket.SubT=TinyType.TINY_PING;inSim.send(pingPacket);

Waiting for packets

There are cases when you want to send a packet and then wait for a response in another packet. There is a helper method sendAwait() which waits for a given packet type and when it's received, it's resolved as a Promise. It also makes sure that the received packet's ReqI property matches the one entered in the sent packet.

import{InSim}from'node-insim';import{IS_TINY,PacketType,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.sendAwait(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_SST,}),PacketType.ISP_STA,).then((packet)=>{console.log(packet.NumConns);});

You can filter the received packet by its data using a callback in the 3rd argument:

import{InSim}from'node-insim';import{IS_TINY,PacketType,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.sendAwait(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_GTP,}),PacketType.ISP_SMALL,({ SubT })=>SubT===SmallType.SMALL_RTP,).then((packet)=>{console.log('session time',packet.UVal);});

Request-reply packet pairs

NameRequest packetReply packet
PingIS_TINY
SubT: TinyType.TINY_PING
IS_TINY
SubT: TinyType.TINY_REPLY
Session timeIS_TINY
SubT: TinyType.TINY_GTP
IS_SMALL
SubT: SmallType.SMALL_RTP
StateIS_TINY
SubT: TinyType.TINY_SST
IS_STA
InSim multiTINY_ISMIS_ISM

Sending messages

The InSim class has helper methods useful for sending messages to LFS.

Send a message which will appear on the local computer only

inSim.sendLocalMessage('Local message');

Send a command

inSim.sendMessage('/end');

Send a message

  • up to 63 characters - send an IS_MST packet
  • 64 characters or more - send an IS_MSX packet
inSim.sendMessage('This is a message');

Send a message to a specific connection by their UCID

inSim.sendMessageToConnection(4,'This is a message targeting UCID 4');

Send a message to a specific player by their PLID

inSim.sendMessageToPlayer(4,'This is a message targeting PLID 4');

Receiving packets

The InSim class exposes an on() method, which is used to listen for incoming packets by their type.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>){console.log(`Connected to LFS ${packet.Product}${packet.Version}`);}

The event callback contains the received packet, and an optional second argument - the InSim instance which received that packet. You can use that instance to send additional packets in response.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType,TinyType}from'node-insim/packets';importtype{IS_TINY}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>,inSim: InSim,){inSim.send(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_PING,}),);}

Multiple hosts

You can use the inSim argument in the event handler callback to identify the source host of the received packets, for instance by the options.Host property.

Alternatively, the InSim class constructor accepts an optional id argument, which can also be used to tell apart the InSim connections.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType}from'node-insim/packets';constinSim1=newInSim('Host One');inSim1.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constinSim2=newInSim('Host Two');inSim2.connect({Host: '127.0.0.2',Port: 30000,IName: 'Node InSim App',});inSim1.on(PacketType.ISP_VER,onVersion);inSim2.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>,inSim: InSim,){console.log(`Connected to ${inSim.options.Host}:${inSim.options.Port}`);if(inSim.id){console.log(`InSim connection ID: ${inSim.id}`);}}

String encoding

All strings in received or sent packets are automatically converted from LFS encoding to Unicode and vice versa.

If you need to access the raw LFS-encoded string in a received packet, use the _raw property in the packet instance, which contains all unconverted string properties.

import{InSim}from'node-insim';import{PacketType}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_ISM,(packet)=>{console.log(packet.HName);// UTF-8 string - ^1Drifter Team ^7★ Serverconsole.log(packet._raw.HName);// raw string - ^1Drifter Team ^7^J�� Server\u0000\u0000\u0000\u0000});

When you send a Unicode string value in a packet, each character will get encoded into the correct LFS encoding, so LFS can display the text in a message or a button.

import{InSim}from'node-insim';import{PacketType}from'node-insim/packets';importtype{IS_MSL}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,(packet)=>{inSim.send(newIS_MSL({Msg: 'čau světe',// LFS will receive: ^Eèau svìte}),);});

Special characters

Special care needs to be taken when sending strings containing caret (^) and slash (/) characters:

  • A caret needs to be escaped as ^^ because the caret symbol acts as an escape character in LFS. Sending ^^hello as a message will display ^hello in LFS. Sending just ^hello would appear as #ello.
  • A slash needs to be escaped as ^s, otherwise it is treated as an LFS in-game command prefix when used in a message packet. Sending ^sjoin as a message will display /join in LFS, whereas sending /join will make the player join the track.

OutGauge

import{OutGauge}from'node-insim';constoutGauge=newOutGauge();outGauge.connect({Host: '127.0.0.1',Port: 29999,});outGauge.on('packet',(data)=>{console.clear();console.log(data.RPM);});

OutSim

import{OutSim,OutSimPack}from'node-insim';constoutSim=newOutSim();outSim.connect({Host: '127.0.0.1',Port: 29999,});outSim.on('packet',(data)=>{// Make sure the simple OutSimPack packet is really received, as opposed to OutSimPack2if(!(datainstanceofOutSimPack)){return;}console.clear();console.log(data.PosX);});

Debugging

Node InSim uses the debug NPM package for debug logs. By default, Node InSim does not output any logs to the standard output.

To enable logging, use the DEBUG environment variable when running your InSim application. All logs are prefixed with node-insim. You can use wildcards to filter out the logs that you need.

DEBUG=* node insim.js # debug all messages
DEBUG=node-insim:tcp node insim.js # debug only TCP protocol messages

Example applications

You can find example applications using Node InSim in the examples folder.

Example
InSim connectionJavaScript + CJSTypeScript + ESM
InSim connection (multiple hosts)JavaScript + CJSTypeScript + ESM
InSim connection (UDP)JavaScript + CJSTypeScript + ESM
OutGaugeJavaScript + CJSTypeScript + ESM
OutGauge with InSim buttonsJavaScript + CJSTypeScript + ESM
OutSimJavaScript + CJSTypeScript + ESM
OutSim with OptionsJavaScript + CJSTypeScript + ESM

Before you run an example, follow the instructions in each example's README.md file.

For instance, to run the "InSim connection - TypeScript" example, run the following commands:

cd examples/typescript/insim-connection
npm install
npm start

Development

Requirements

  • Node.js 18
  • Yarn

Start a development server

yarn dev

Code generators

When adding new InSim packets to the library, you can use built-in code generators using yarn generate. It will create and update all the necessary files for you.

Run unit tests

yarn test

Run tests against a real LFS application

To run these tests, LFS must be running with an InSim port open.

By default, the tests connect to 127.0.0.1:29999. The InSim host and port can be configured by copying .env to .env.local in the lfs-test directory.

yarn test:lfs

Build all example applications

This command will go through each application in the examples/ folder, install its dependencies, then build the application (typescript only).

yarn test:examples

Lint code

yarn lint

Format code

yarn format

Production build

Compiled files will be created in dist/.

yarn build

Run all checks at once

You can run code format, lint + fix, build and test with the following command:

yarn check-all

Releases

This project uses Changesets to manage publishing and release notes.

Making changes

Before committing a bug fix, a new feature or a breaking change, run the following command to create a changeset:

yarn changeset
  1. When prompted, choose if it's a patch, minor or a major change.
  2. Enter a summary for the change which will appear in the changelogs.
  3. When confirmed, a changeset Markdown file will be created in the .changeset directory.
  4. Commit the new changeset file along with your source code changes.

Releasing a new version

To bump the package version:

  1. Go to the GitHub Actions page of this repo.
  2. Select the Release workflow.
  3. Click "Run workflow".

This will bump Node InSim version according to the types of changes in the changesets and create a pull request. You can review the changes and merge the pull request once it's ready.

Once the version has been bumped, trigger the Release workflow again, which will publish a new version to NPM.


Node Insim - An open source project by Sim Broadcasts

About

A Node.js library for Live For Speed InSim protocol

Topics

Resources

Stars

16 stars

Watchers

3 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

634 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Node InSim

NPM VersionNode.js CI

An InSim library for Node.js with TypeScript support.

Introduction

Node InSim provides a JavaScript API to communicate with the Live for Speed InSim protocol over a TCP connection. After connecting to an LFS host via a hostname and a port, you are able to send InSim packets to the host and receive incoming packets from the host.

All packet structures in Node InSim are identical to the structs defined in the InSim protocol. All packet classes with all their properties are documented according to the specification.

InSim compatibility

Node InSim is compatible with InSim version 10.

Installation

Install the node-insim NPM package in your Node.js application:

npm install --save node-insim

or if you use Yarn:

yarn add node-insim

Documentation

For more detailed documentation of the public API, see https://simbroadcasts.github.io/node-insim/.

Usage

Connecting

To connect to an LFS host, you must enter its hostname, a port and a short name of your InSim application.

The InSim port must be configured in the LFS host settings. Also, make sure the public IP address from which your application is connecting is allowed to connect to the host's InSim port.

Single host

import{InSim}from'node-insim';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});

Multiple hosts

To connect to multiple hosts at once, create a new InSim instance for each host.

import{InSim}from'node-insim';constinSim1=newInSim();inSim1.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constinSim2=newInSim();inSim2.connect({Host: '127.0.0.2',Port: 30000,IName: 'Node InSim App',});

TCP / UDP

By default, Node InSim opens a TCP connection. If you want to use UDP, set the Protocol option to UDP in the connect function.

import{InSim}from'node-insim';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',Protocol: 'UDP',});

Sending packets

InSim packets can be sent using the send() method on the InSim class instance, which takes a single argument - the packet class instance.

A fast way to set packet properties is to populate them in the class constructor:

import{InSim}from'node-insim';import{IS_TINY,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.send(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_PING,}),);

Another way is to assign each property after creating the instance:

import{InSim}from'node-insim';import{IS_TINY,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constpingPacket=newIS_TINY();pingPacket.ReqI=1;pingPacket.SubT=TinyType.TINY_PING;inSim.send(pingPacket);

Waiting for packets

There are cases when you want to send a packet and then wait for a response in another packet. There is a helper method sendAwait() which waits for a given packet type and when it's received, it's resolved as a Promise. It also makes sure that the received packet's ReqI property matches the one entered in the sent packet.

import{InSim}from'node-insim';import{IS_TINY,PacketType,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.sendAwait(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_SST,}),PacketType.ISP_STA,).then((packet)=>{console.log(packet.NumConns);});

You can filter the received packet by its data using a callback in the 3rd argument:

import{InSim}from'node-insim';import{IS_TINY,PacketType,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.sendAwait(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_GTP,}),PacketType.ISP_SMALL,({ SubT })=>SubT===SmallType.SMALL_RTP,).then((packet)=>{console.log('session time',packet.UVal);});

Request-reply packet pairs

NameRequest packetReply packet
PingIS_TINY
SubT: TinyType.TINY_PING
IS_TINY
SubT: TinyType.TINY_REPLY
Session timeIS_TINY
SubT: TinyType.TINY_GTP
IS_SMALL
SubT: SmallType.SMALL_RTP
StateIS_TINY
SubT: TinyType.TINY_SST
IS_STA
InSim multiTINY_ISMIS_ISM

Sending messages

The InSim class has helper methods useful for sending messages to LFS.

Send a message which will appear on the local computer only

inSim.sendLocalMessage('Local message');

Send a command

inSim.sendMessage('/end');

Send a message

  • up to 63 characters - send an IS_MST packet
  • 64 characters or more - send an IS_MSX packet
inSim.sendMessage('This is a message');

Send a message to a specific connection by their UCID

inSim.sendMessageToConnection(4,'This is a message targeting UCID 4');

Send a message to a specific player by their PLID

inSim.sendMessageToPlayer(4,'This is a message targeting PLID 4');

Receiving packets

The InSim class exposes an on() method, which is used to listen for incoming packets by their type.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>){console.log(`Connected to LFS ${packet.Product}${packet.Version}`);}

The event callback contains the received packet, and an optional second argument - the InSim instance which received that packet. You can use that instance to send additional packets in response.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType,TinyType}from'node-insim/packets';importtype{IS_TINY}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>,inSim: InSim,){inSim.send(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_PING,}),);}

Multiple hosts

You can use the inSim argument in the event handler callback to identify the source host of the received packets, for instance by the options.Host property.

Alternatively, the InSim class constructor accepts an optional id argument, which can also be used to tell apart the InSim connections.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType}from'node-insim/packets';constinSim1=newInSim('Host One');inSim1.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constinSim2=newInSim('Host Two');inSim2.connect({Host: '127.0.0.2',Port: 30000,IName: 'Node InSim App',});inSim1.on(PacketType.ISP_VER,onVersion);inSim2.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>,inSim: InSim,){console.log(`Connected to ${inSim.options.Host}:${inSim.options.Port}`);if(inSim.id){console.log(`InSim connection ID: ${inSim.id}`);}}

String encoding

All strings in received or sent packets are automatically converted from LFS encoding to Unicode and vice versa.

If you need to access the raw LFS-encoded string in a received packet, use the _raw property in the packet instance, which contains all unconverted string properties.

import{InSim}from'node-insim';import{PacketType}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_ISM,(packet)=>{console.log(packet.HName);// UTF-8 string - ^1Drifter Team ^7★ Serverconsole.log(packet._raw.HName);// raw string - ^1Drifter Team ^7^J�� Server\u0000\u0000\u0000\u0000});

When you send a Unicode string value in a packet, each character will get encoded into the correct LFS encoding, so LFS can display the text in a message or a button.

import{InSim}from'node-insim';import{PacketType}from'node-insim/packets';importtype{IS_MSL}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,(packet)=>{inSim.send(newIS_MSL({Msg: 'čau světe',// LFS will receive: ^Eèau svìte}),);});

Special characters

Special care needs to be taken when sending strings containing caret (^) and slash (/) characters:

  • A caret needs to be escaped as ^^ because the caret symbol acts as an escape character in LFS. Sending ^^hello as a message will display ^hello in LFS. Sending just ^hello would appear as #ello.
  • A slash needs to be escaped as ^s, otherwise it is treated as an LFS in-game command prefix when used in a message packet. Sending ^sjoin as a message will display /join in LFS, whereas sending /join will make the player join the track.

OutGauge

import{OutGauge}from'node-insim';constoutGauge=newOutGauge();outGauge.connect({Host: '127.0.0.1',Port: 29999,});outGauge.on('packet',(data)=>{console.clear();console.log(data.RPM);});

OutSim

import{OutSim,OutSimPack}from'node-insim';constoutSim=newOutSim();outSim.connect({Host: '127.0.0.1',Port: 29999,});outSim.on('packet',(data)=>{// Make sure the simple OutSimPack packet is really received, as opposed to OutSimPack2if(!(datainstanceofOutSimPack)){return;}console.clear();console.log(data.PosX);});

Debugging

Node InSim uses the debug NPM package for debug logs. By default, Node InSim does not output any logs to the standard output.

To enable logging, use the DEBUG environment variable when running your InSim application. All logs are prefixed with node-insim. You can use wildcards to filter out the logs that you need.

DEBUG=* node insim.js # debug all messages
DEBUG=node-insim:tcp node insim.js # debug only TCP protocol messages

Example applications

You can find example applications using Node InSim in the examples folder.

Example
InSim connectionJavaScript + CJSTypeScript + ESM
InSim connection (multiple hosts)JavaScript + CJSTypeScript + ESM
InSim connection (UDP)JavaScript + CJSTypeScript + ESM
OutGaugeJavaScript + CJSTypeScript + ESM
OutGauge with InSim buttonsJavaScript + CJSTypeScript + ESM
OutSimJavaScript + CJSTypeScript + ESM
OutSim with OptionsJavaScript + CJSTypeScript + ESM

Before you run an example, follow the instructions in each example's README.md file.

For instance, to run the "InSim connection - TypeScript" example, run the following commands:

cd examples/typescript/insim-connection
npm install
npm start

Development

Requirements

  • Node.js 18
  • Yarn

Start a development server

yarn dev

Code generators

When adding new InSim packets to the library, you can use built-in code generators using yarn generate. It will create and update all the necessary files for you.

Run unit tests

yarn test

Run tests against a real LFS application

To run these tests, LFS must be running with an InSim port open.

By default, the tests connect to 127.0.0.1:29999. The InSim host and port can be configured by copying .env to .env.local in the lfs-test directory.

yarn test:lfs

Build all example applications

This command will go through each application in the examples/ folder, install its dependencies, then build the application (typescript only).

yarn test:examples

Lint code

yarn lint

Format code

yarn format

Production build

Compiled files will be created in dist/.

yarn build

Run all checks at once

You can run code format, lint + fix, build and test with the following command:

yarn check-all

Releases

This project uses Changesets to manage publishing and release notes.

Making changes

Before committing a bug fix, a new feature or a breaking change, run the following command to create a changeset:

yarn changeset
  1. When prompted, choose if it's a patch, minor or a major change.
  2. Enter a summary for the change which will appear in the changelogs.
  3. When confirmed, a changeset Markdown file will be created in the .changeset directory.
  4. Commit the new changeset file along with your source code changes.

Releasing a new version

To bump the package version:

  1. Go to the GitHub Actions page of this repo.
  2. Select the Release workflow.
  3. Click "Run workflow".

This will bump Node InSim version according to the types of changes in the changesets and create a pull request. You can review the changes and merge the pull request once it's ready.

Once the version has been bumped, trigger the Release workflow again, which will publish a new version to NPM.


Node Insim - An open source project by Sim Broadcasts

About

A Node.js library for Live For Speed InSim protocol

Topics

Resources

Stars

16 stars

Watchers

3 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

634 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Node InSim

NPM VersionNode.js CI

An InSim library for Node.js with TypeScript support.

Introduction

Node InSim provides a JavaScript API to communicate with the Live for Speed InSim protocol over a TCP connection. After connecting to an LFS host via a hostname and a port, you are able to send InSim packets to the host and receive incoming packets from the host.

All packet structures in Node InSim are identical to the structs defined in the InSim protocol. All packet classes with all their properties are documented according to the specification.

InSim compatibility

Node InSim is compatible with InSim version 10.

Installation

Install the node-insim NPM package in your Node.js application:

npm install --save node-insim

or if you use Yarn:

yarn add node-insim

Documentation

For more detailed documentation of the public API, see https://simbroadcasts.github.io/node-insim/.

Usage

Connecting

To connect to an LFS host, you must enter its hostname, a port and a short name of your InSim application.

The InSim port must be configured in the LFS host settings. Also, make sure the public IP address from which your application is connecting is allowed to connect to the host's InSim port.

Single host

import{InSim}from'node-insim';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});

Multiple hosts

To connect to multiple hosts at once, create a new InSim instance for each host.

import{InSim}from'node-insim';constinSim1=newInSim();inSim1.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constinSim2=newInSim();inSim2.connect({Host: '127.0.0.2',Port: 30000,IName: 'Node InSim App',});

TCP / UDP

By default, Node InSim opens a TCP connection. If you want to use UDP, set the Protocol option to UDP in the connect function.

import{InSim}from'node-insim';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',Protocol: 'UDP',});

Sending packets

InSim packets can be sent using the send() method on the InSim class instance, which takes a single argument - the packet class instance.

A fast way to set packet properties is to populate them in the class constructor:

import{InSim}from'node-insim';import{IS_TINY,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.send(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_PING,}),);

Another way is to assign each property after creating the instance:

import{InSim}from'node-insim';import{IS_TINY,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constpingPacket=newIS_TINY();pingPacket.ReqI=1;pingPacket.SubT=TinyType.TINY_PING;inSim.send(pingPacket);

Waiting for packets

There are cases when you want to send a packet and then wait for a response in another packet. There is a helper method sendAwait() which waits for a given packet type and when it's received, it's resolved as a Promise. It also makes sure that the received packet's ReqI property matches the one entered in the sent packet.

import{InSim}from'node-insim';import{IS_TINY,PacketType,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.sendAwait(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_SST,}),PacketType.ISP_STA,).then((packet)=>{console.log(packet.NumConns);});

You can filter the received packet by its data using a callback in the 3rd argument:

import{InSim}from'node-insim';import{IS_TINY,PacketType,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.sendAwait(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_GTP,}),PacketType.ISP_SMALL,({ SubT })=>SubT===SmallType.SMALL_RTP,).then((packet)=>{console.log('session time',packet.UVal);});

Request-reply packet pairs

NameRequest packetReply packet
PingIS_TINY
SubT: TinyType.TINY_PING
IS_TINY
SubT: TinyType.TINY_REPLY
Session timeIS_TINY
SubT: TinyType.TINY_GTP
IS_SMALL
SubT: SmallType.SMALL_RTP
StateIS_TINY
SubT: TinyType.TINY_SST
IS_STA
InSim multiTINY_ISMIS_ISM

Sending messages

The InSim class has helper methods useful for sending messages to LFS.

Send a message which will appear on the local computer only

inSim.sendLocalMessage('Local message');

Send a command

inSim.sendMessage('/end');

Send a message

  • up to 63 characters - send an IS_MST packet
  • 64 characters or more - send an IS_MSX packet
inSim.sendMessage('This is a message');

Send a message to a specific connection by their UCID

inSim.sendMessageToConnection(4,'This is a message targeting UCID 4');

Send a message to a specific player by their PLID

inSim.sendMessageToPlayer(4,'This is a message targeting PLID 4');

Receiving packets

The InSim class exposes an on() method, which is used to listen for incoming packets by their type.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>){console.log(`Connected to LFS ${packet.Product}${packet.Version}`);}

The event callback contains the received packet, and an optional second argument - the InSim instance which received that packet. You can use that instance to send additional packets in response.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType,TinyType}from'node-insim/packets';importtype{IS_TINY}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>,inSim: InSim,){inSim.send(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_PING,}),);}

Multiple hosts

You can use the inSim argument in the event handler callback to identify the source host of the received packets, for instance by the options.Host property.

Alternatively, the InSim class constructor accepts an optional id argument, which can also be used to tell apart the InSim connections.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType}from'node-insim/packets';constinSim1=newInSim('Host One');inSim1.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constinSim2=newInSim('Host Two');inSim2.connect({Host: '127.0.0.2',Port: 30000,IName: 'Node InSim App',});inSim1.on(PacketType.ISP_VER,onVersion);inSim2.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>,inSim: InSim,){console.log(`Connected to ${inSim.options.Host}:${inSim.options.Port}`);if(inSim.id){console.log(`InSim connection ID: ${inSim.id}`);}}

String encoding

All strings in received or sent packets are automatically converted from LFS encoding to Unicode and vice versa.

If you need to access the raw LFS-encoded string in a received packet, use the _raw property in the packet instance, which contains all unconverted string properties.

import{InSim}from'node-insim';import{PacketType}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_ISM,(packet)=>{console.log(packet.HName);// UTF-8 string - ^1Drifter Team ^7★ Serverconsole.log(packet._raw.HName);// raw string - ^1Drifter Team ^7^J�� Server\u0000\u0000\u0000\u0000});

When you send a Unicode string value in a packet, each character will get encoded into the correct LFS encoding, so LFS can display the text in a message or a button.

import{InSim}from'node-insim';import{PacketType}from'node-insim/packets';importtype{IS_MSL}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,(packet)=>{inSim.send(newIS_MSL({Msg: 'čau světe',// LFS will receive: ^Eèau svìte}),);});

Special characters

Special care needs to be taken when sending strings containing caret (^) and slash (/) characters:

  • A caret needs to be escaped as ^^ because the caret symbol acts as an escape character in LFS. Sending ^^hello as a message will display ^hello in LFS. Sending just ^hello would appear as #ello.
  • A slash needs to be escaped as ^s, otherwise it is treated as an LFS in-game command prefix when used in a message packet. Sending ^sjoin as a message will display /join in LFS, whereas sending /join will make the player join the track.

OutGauge

import{OutGauge}from'node-insim';constoutGauge=newOutGauge();outGauge.connect({Host: '127.0.0.1',Port: 29999,});outGauge.on('packet',(data)=>{console.clear();console.log(data.RPM);});

OutSim

import{OutSim,OutSimPack}from'node-insim';constoutSim=newOutSim();outSim.connect({Host: '127.0.0.1',Port: 29999,});outSim.on('packet',(data)=>{// Make sure the simple OutSimPack packet is really received, as opposed to OutSimPack2if(!(datainstanceofOutSimPack)){return;}console.clear();console.log(data.PosX);});

Debugging

Node InSim uses the debug NPM package for debug logs. By default, Node InSim does not output any logs to the standard output.

To enable logging, use the DEBUG environment variable when running your InSim application. All logs are prefixed with node-insim. You can use wildcards to filter out the logs that you need.

DEBUG=* node insim.js # debug all messages
DEBUG=node-insim:tcp node insim.js # debug only TCP protocol messages

Example applications

You can find example applications using Node InSim in the examples folder.

Example
InSim connectionJavaScript + CJSTypeScript + ESM
InSim connection (multiple hosts)JavaScript + CJSTypeScript + ESM
InSim connection (UDP)JavaScript + CJSTypeScript + ESM
OutGaugeJavaScript + CJSTypeScript + ESM
OutGauge with InSim buttonsJavaScript + CJSTypeScript + ESM
OutSimJavaScript + CJSTypeScript + ESM
OutSim with OptionsJavaScript + CJSTypeScript + ESM

Before you run an example, follow the instructions in each example's README.md file.

For instance, to run the "InSim connection - TypeScript" example, run the following commands:

cd examples/typescript/insim-connection
npm install
npm start

Development

Requirements

  • Node.js 18
  • Yarn

Start a development server

yarn dev

Code generators

When adding new InSim packets to the library, you can use built-in code generators using yarn generate. It will create and update all the necessary files for you.

Run unit tests

yarn test

Run tests against a real LFS application

To run these tests, LFS must be running with an InSim port open.

By default, the tests connect to 127.0.0.1:29999. The InSim host and port can be configured by copying .env to .env.local in the lfs-test directory.

yarn test:lfs

Build all example applications

This command will go through each application in the examples/ folder, install its dependencies, then build the application (typescript only).

yarn test:examples

Lint code

yarn lint

Format code

yarn format

Production build

Compiled files will be created in dist/.

yarn build

Run all checks at once

You can run code format, lint + fix, build and test with the following command:

yarn check-all

Releases

This project uses Changesets to manage publishing and release notes.

Making changes

Before committing a bug fix, a new feature or a breaking change, run the following command to create a changeset:

yarn changeset
  1. When prompted, choose if it's a patch, minor or a major change.
  2. Enter a summary for the change which will appear in the changelogs.
  3. When confirmed, a changeset Markdown file will be created in the .changeset directory.
  4. Commit the new changeset file along with your source code changes.

Releasing a new version

To bump the package version:

  1. Go to the GitHub Actions page of this repo.
  2. Select the Release workflow.
  3. Click "Run workflow".

This will bump Node InSim version according to the types of changes in the changesets and create a pull request. You can review the changes and merge the pull request once it's ready.

Once the version has been bumped, trigger the Release workflow again, which will publish a new version to NPM.


Node Insim - An open source project by Sim Broadcasts

About

A Node.js library for Live For Speed InSim protocol

Topics

Resources

Stars

16 stars

Watchers

3 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

634 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Node InSim

NPM VersionNode.js CI

An InSim library for Node.js with TypeScript support.

Introduction

Node InSim provides a JavaScript API to communicate with the Live for Speed InSim protocol over a TCP connection. After connecting to an LFS host via a hostname and a port, you are able to send InSim packets to the host and receive incoming packets from the host.

All packet structures in Node InSim are identical to the structs defined in the InSim protocol. All packet classes with all their properties are documented according to the specification.

InSim compatibility

Node InSim is compatible with InSim version 10.

Installation

Install the node-insim NPM package in your Node.js application:

npm install --save node-insim

or if you use Yarn:

yarn add node-insim

Documentation

For more detailed documentation of the public API, see https://simbroadcasts.github.io/node-insim/.

Usage

Connecting

To connect to an LFS host, you must enter its hostname, a port and a short name of your InSim application.

The InSim port must be configured in the LFS host settings. Also, make sure the public IP address from which your application is connecting is allowed to connect to the host's InSim port.

Single host

import{InSim}from'node-insim';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});

Multiple hosts

To connect to multiple hosts at once, create a new InSim instance for each host.

import{InSim}from'node-insim';constinSim1=newInSim();inSim1.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constinSim2=newInSim();inSim2.connect({Host: '127.0.0.2',Port: 30000,IName: 'Node InSim App',});

TCP / UDP

By default, Node InSim opens a TCP connection. If you want to use UDP, set the Protocol option to UDP in the connect function.

import{InSim}from'node-insim';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',Protocol: 'UDP',});

Sending packets

InSim packets can be sent using the send() method on the InSim class instance, which takes a single argument - the packet class instance.

A fast way to set packet properties is to populate them in the class constructor:

import{InSim}from'node-insim';import{IS_TINY,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.send(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_PING,}),);

Another way is to assign each property after creating the instance:

import{InSim}from'node-insim';import{IS_TINY,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constpingPacket=newIS_TINY();pingPacket.ReqI=1;pingPacket.SubT=TinyType.TINY_PING;inSim.send(pingPacket);

Waiting for packets

There are cases when you want to send a packet and then wait for a response in another packet. There is a helper method sendAwait() which waits for a given packet type and when it's received, it's resolved as a Promise. It also makes sure that the received packet's ReqI property matches the one entered in the sent packet.

import{InSim}from'node-insim';import{IS_TINY,PacketType,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.sendAwait(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_SST,}),PacketType.ISP_STA,).then((packet)=>{console.log(packet.NumConns);});

You can filter the received packet by its data using a callback in the 3rd argument:

import{InSim}from'node-insim';import{IS_TINY,PacketType,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.sendAwait(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_GTP,}),PacketType.ISP_SMALL,({ SubT })=>SubT===SmallType.SMALL_RTP,).then((packet)=>{console.log('session time',packet.UVal);});

Request-reply packet pairs

NameRequest packetReply packet
PingIS_TINY
SubT: TinyType.TINY_PING
IS_TINY
SubT: TinyType.TINY_REPLY
Session timeIS_TINY
SubT: TinyType.TINY_GTP
IS_SMALL
SubT: SmallType.SMALL_RTP
StateIS_TINY
SubT: TinyType.TINY_SST
IS_STA
InSim multiTINY_ISMIS_ISM

Sending messages

The InSim class has helper methods useful for sending messages to LFS.

Send a message which will appear on the local computer only

inSim.sendLocalMessage('Local message');

Send a command

inSim.sendMessage('/end');

Send a message

  • up to 63 characters - send an IS_MST packet
  • 64 characters or more - send an IS_MSX packet
inSim.sendMessage('This is a message');

Send a message to a specific connection by their UCID

inSim.sendMessageToConnection(4,'This is a message targeting UCID 4');

Send a message to a specific player by their PLID

inSim.sendMessageToPlayer(4,'This is a message targeting PLID 4');

Receiving packets

The InSim class exposes an on() method, which is used to listen for incoming packets by their type.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>){console.log(`Connected to LFS ${packet.Product}${packet.Version}`);}

The event callback contains the received packet, and an optional second argument - the InSim instance which received that packet. You can use that instance to send additional packets in response.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType,TinyType}from'node-insim/packets';importtype{IS_TINY}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>,inSim: InSim,){inSim.send(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_PING,}),);}

Multiple hosts

You can use the inSim argument in the event handler callback to identify the source host of the received packets, for instance by the options.Host property.

Alternatively, the InSim class constructor accepts an optional id argument, which can also be used to tell apart the InSim connections.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType}from'node-insim/packets';constinSim1=newInSim('Host One');inSim1.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constinSim2=newInSim('Host Two');inSim2.connect({Host: '127.0.0.2',Port: 30000,IName: 'Node InSim App',});inSim1.on(PacketType.ISP_VER,onVersion);inSim2.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>,inSim: InSim,){console.log(`Connected to ${inSim.options.Host}:${inSim.options.Port}`);if(inSim.id){console.log(`InSim connection ID: ${inSim.id}`);}}

String encoding

All strings in received or sent packets are automatically converted from LFS encoding to Unicode and vice versa.

If you need to access the raw LFS-encoded string in a received packet, use the _raw property in the packet instance, which contains all unconverted string properties.

import{InSim}from'node-insim';import{PacketType}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_ISM,(packet)=>{console.log(packet.HName);// UTF-8 string - ^1Drifter Team ^7★ Serverconsole.log(packet._raw.HName);// raw string - ^1Drifter Team ^7^J�� Server\u0000\u0000\u0000\u0000});

When you send a Unicode string value in a packet, each character will get encoded into the correct LFS encoding, so LFS can display the text in a message or a button.

import{InSim}from'node-insim';import{PacketType}from'node-insim/packets';importtype{IS_MSL}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,(packet)=>{inSim.send(newIS_MSL({Msg: 'čau světe',// LFS will receive: ^Eèau svìte}),);});

Special characters

Special care needs to be taken when sending strings containing caret (^) and slash (/) characters:

  • A caret needs to be escaped as ^^ because the caret symbol acts as an escape character in LFS. Sending ^^hello as a message will display ^hello in LFS. Sending just ^hello would appear as #ello.
  • A slash needs to be escaped as ^s, otherwise it is treated as an LFS in-game command prefix when used in a message packet. Sending ^sjoin as a message will display /join in LFS, whereas sending /join will make the player join the track.

OutGauge

import{OutGauge}from'node-insim';constoutGauge=newOutGauge();outGauge.connect({Host: '127.0.0.1',Port: 29999,});outGauge.on('packet',(data)=>{console.clear();console.log(data.RPM);});

OutSim

import{OutSim,OutSimPack}from'node-insim';constoutSim=newOutSim();outSim.connect({Host: '127.0.0.1',Port: 29999,});outSim.on('packet',(data)=>{// Make sure the simple OutSimPack packet is really received, as opposed to OutSimPack2if(!(datainstanceofOutSimPack)){return;}console.clear();console.log(data.PosX);});

Debugging

Node InSim uses the debug NPM package for debug logs. By default, Node InSim does not output any logs to the standard output.

To enable logging, use the DEBUG environment variable when running your InSim application. All logs are prefixed with node-insim. You can use wildcards to filter out the logs that you need.

DEBUG=* node insim.js # debug all messages
DEBUG=node-insim:tcp node insim.js # debug only TCP protocol messages

Example applications

You can find example applications using Node InSim in the examples folder.

Example
InSim connectionJavaScript + CJSTypeScript + ESM
InSim connection (multiple hosts)JavaScript + CJSTypeScript + ESM
InSim connection (UDP)JavaScript + CJSTypeScript + ESM
OutGaugeJavaScript + CJSTypeScript + ESM
OutGauge with InSim buttonsJavaScript + CJSTypeScript + ESM
OutSimJavaScript + CJSTypeScript + ESM
OutSim with OptionsJavaScript + CJSTypeScript + ESM

Before you run an example, follow the instructions in each example's README.md file.

For instance, to run the "InSim connection - TypeScript" example, run the following commands:

cd examples/typescript/insim-connection
npm install
npm start

Development

Requirements

  • Node.js 18
  • Yarn

Start a development server

yarn dev

Code generators

When adding new InSim packets to the library, you can use built-in code generators using yarn generate. It will create and update all the necessary files for you.

Run unit tests

yarn test

Run tests against a real LFS application

To run these tests, LFS must be running with an InSim port open.

By default, the tests connect to 127.0.0.1:29999. The InSim host and port can be configured by copying .env to .env.local in the lfs-test directory.

yarn test:lfs

Build all example applications

This command will go through each application in the examples/ folder, install its dependencies, then build the application (typescript only).

yarn test:examples

Lint code

yarn lint

Format code

yarn format

Production build

Compiled files will be created in dist/.

yarn build

Run all checks at once

You can run code format, lint + fix, build and test with the following command:

yarn check-all

Releases

This project uses Changesets to manage publishing and release notes.

Making changes

Before committing a bug fix, a new feature or a breaking change, run the following command to create a changeset:

yarn changeset
  1. When prompted, choose if it's a patch, minor or a major change.
  2. Enter a summary for the change which will appear in the changelogs.
  3. When confirmed, a changeset Markdown file will be created in the .changeset directory.
  4. Commit the new changeset file along with your source code changes.

Releasing a new version

To bump the package version:

  1. Go to the GitHub Actions page of this repo.
  2. Select the Release workflow.
  3. Click "Run workflow".

This will bump Node InSim version according to the types of changes in the changesets and create a pull request. You can review the changes and merge the pull request once it's ready.

Once the version has been bumped, trigger the Release workflow again, which will publish a new version to NPM.


Node Insim - An open source project by Sim Broadcasts

About

A Node.js library for Live For Speed InSim protocol

Topics

Resources

Stars

16 stars

Watchers

3 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

634 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Node InSim

NPM VersionNode.js CI

An InSim library for Node.js with TypeScript support.

Introduction

Node InSim provides a JavaScript API to communicate with the Live for Speed InSim protocol over a TCP connection. After connecting to an LFS host via a hostname and a port, you are able to send InSim packets to the host and receive incoming packets from the host.

All packet structures in Node InSim are identical to the structs defined in the InSim protocol. All packet classes with all their properties are documented according to the specification.

InSim compatibility

Node InSim is compatible with InSim version 10.

Installation

Install the node-insim NPM package in your Node.js application:

npm install --save node-insim

or if you use Yarn:

yarn add node-insim

Documentation

For more detailed documentation of the public API, see https://simbroadcasts.github.io/node-insim/.

Usage

Connecting

To connect to an LFS host, you must enter its hostname, a port and a short name of your InSim application.

The InSim port must be configured in the LFS host settings. Also, make sure the public IP address from which your application is connecting is allowed to connect to the host's InSim port.

Single host

import{InSim}from'node-insim';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});

Multiple hosts

To connect to multiple hosts at once, create a new InSim instance for each host.

import{InSim}from'node-insim';constinSim1=newInSim();inSim1.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constinSim2=newInSim();inSim2.connect({Host: '127.0.0.2',Port: 30000,IName: 'Node InSim App',});

TCP / UDP

By default, Node InSim opens a TCP connection. If you want to use UDP, set the Protocol option to UDP in the connect function.

import{InSim}from'node-insim';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',Protocol: 'UDP',});

Sending packets

InSim packets can be sent using the send() method on the InSim class instance, which takes a single argument - the packet class instance.

A fast way to set packet properties is to populate them in the class constructor:

import{InSim}from'node-insim';import{IS_TINY,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.send(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_PING,}),);

Another way is to assign each property after creating the instance:

import{InSim}from'node-insim';import{IS_TINY,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constpingPacket=newIS_TINY();pingPacket.ReqI=1;pingPacket.SubT=TinyType.TINY_PING;inSim.send(pingPacket);

Waiting for packets

There are cases when you want to send a packet and then wait for a response in another packet. There is a helper method sendAwait() which waits for a given packet type and when it's received, it's resolved as a Promise. It also makes sure that the received packet's ReqI property matches the one entered in the sent packet.

import{InSim}from'node-insim';import{IS_TINY,PacketType,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.sendAwait(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_SST,}),PacketType.ISP_STA,).then((packet)=>{console.log(packet.NumConns);});

You can filter the received packet by its data using a callback in the 3rd argument:

import{InSim}from'node-insim';import{IS_TINY,PacketType,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.sendAwait(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_GTP,}),PacketType.ISP_SMALL,({ SubT })=>SubT===SmallType.SMALL_RTP,).then((packet)=>{console.log('session time',packet.UVal);});

Request-reply packet pairs

NameRequest packetReply packet
PingIS_TINY
SubT: TinyType.TINY_PING
IS_TINY
SubT: TinyType.TINY_REPLY
Session timeIS_TINY
SubT: TinyType.TINY_GTP
IS_SMALL
SubT: SmallType.SMALL_RTP
StateIS_TINY
SubT: TinyType.TINY_SST
IS_STA
InSim multiTINY_ISMIS_ISM

Sending messages

The InSim class has helper methods useful for sending messages to LFS.

Send a message which will appear on the local computer only

inSim.sendLocalMessage('Local message');

Send a command

inSim.sendMessage('/end');

Send a message

  • up to 63 characters - send an IS_MST packet
  • 64 characters or more - send an IS_MSX packet
inSim.sendMessage('This is a message');

Send a message to a specific connection by their UCID

inSim.sendMessageToConnection(4,'This is a message targeting UCID 4');

Send a message to a specific player by their PLID

inSim.sendMessageToPlayer(4,'This is a message targeting PLID 4');

Receiving packets

The InSim class exposes an on() method, which is used to listen for incoming packets by their type.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>){console.log(`Connected to LFS ${packet.Product}${packet.Version}`);}

The event callback contains the received packet, and an optional second argument - the InSim instance which received that packet. You can use that instance to send additional packets in response.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType,TinyType}from'node-insim/packets';importtype{IS_TINY}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>,inSim: InSim,){inSim.send(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_PING,}),);}

Multiple hosts

You can use the inSim argument in the event handler callback to identify the source host of the received packets, for instance by the options.Host property.

Alternatively, the InSim class constructor accepts an optional id argument, which can also be used to tell apart the InSim connections.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType}from'node-insim/packets';constinSim1=newInSim('Host One');inSim1.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constinSim2=newInSim('Host Two');inSim2.connect({Host: '127.0.0.2',Port: 30000,IName: 'Node InSim App',});inSim1.on(PacketType.ISP_VER,onVersion);inSim2.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>,inSim: InSim,){console.log(`Connected to ${inSim.options.Host}:${inSim.options.Port}`);if(inSim.id){console.log(`InSim connection ID: ${inSim.id}`);}}

String encoding

All strings in received or sent packets are automatically converted from LFS encoding to Unicode and vice versa.

If you need to access the raw LFS-encoded string in a received packet, use the _raw property in the packet instance, which contains all unconverted string properties.

import{InSim}from'node-insim';import{PacketType}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_ISM,(packet)=>{console.log(packet.HName);// UTF-8 string - ^1Drifter Team ^7★ Serverconsole.log(packet._raw.HName);// raw string - ^1Drifter Team ^7^J�� Server\u0000\u0000\u0000\u0000});

When you send a Unicode string value in a packet, each character will get encoded into the correct LFS encoding, so LFS can display the text in a message or a button.

import{InSim}from'node-insim';import{PacketType}from'node-insim/packets';importtype{IS_MSL}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,(packet)=>{inSim.send(newIS_MSL({Msg: 'čau světe',// LFS will receive: ^Eèau svìte}),);});

Special characters

Special care needs to be taken when sending strings containing caret (^) and slash (/) characters:

  • A caret needs to be escaped as ^^ because the caret symbol acts as an escape character in LFS. Sending ^^hello as a message will display ^hello in LFS. Sending just ^hello would appear as #ello.
  • A slash needs to be escaped as ^s, otherwise it is treated as an LFS in-game command prefix when used in a message packet. Sending ^sjoin as a message will display /join in LFS, whereas sending /join will make the player join the track.

OutGauge

import{OutGauge}from'node-insim';constoutGauge=newOutGauge();outGauge.connect({Host: '127.0.0.1',Port: 29999,});outGauge.on('packet',(data)=>{console.clear();console.log(data.RPM);});

OutSim

import{OutSim,OutSimPack}from'node-insim';constoutSim=newOutSim();outSim.connect({Host: '127.0.0.1',Port: 29999,});outSim.on('packet',(data)=>{// Make sure the simple OutSimPack packet is really received, as opposed to OutSimPack2if(!(datainstanceofOutSimPack)){return;}console.clear();console.log(data.PosX);});

Debugging

Node InSim uses the debug NPM package for debug logs. By default, Node InSim does not output any logs to the standard output.

To enable logging, use the DEBUG environment variable when running your InSim application. All logs are prefixed with node-insim. You can use wildcards to filter out the logs that you need.

DEBUG=* node insim.js # debug all messages
DEBUG=node-insim:tcp node insim.js # debug only TCP protocol messages

Example applications

You can find example applications using Node InSim in the examples folder.

Example
InSim connectionJavaScript + CJSTypeScript + ESM
InSim connection (multiple hosts)JavaScript + CJSTypeScript + ESM
InSim connection (UDP)JavaScript + CJSTypeScript + ESM
OutGaugeJavaScript + CJSTypeScript + ESM
OutGauge with InSim buttonsJavaScript + CJSTypeScript + ESM
OutSimJavaScript + CJSTypeScript + ESM
OutSim with OptionsJavaScript + CJSTypeScript + ESM

Before you run an example, follow the instructions in each example's README.md file.

For instance, to run the "InSim connection - TypeScript" example, run the following commands:

cd examples/typescript/insim-connection
npm install
npm start

Development

Requirements

  • Node.js 18
  • Yarn

Start a development server

yarn dev

Code generators

When adding new InSim packets to the library, you can use built-in code generators using yarn generate. It will create and update all the necessary files for you.

Run unit tests

yarn test

Run tests against a real LFS application

To run these tests, LFS must be running with an InSim port open.

By default, the tests connect to 127.0.0.1:29999. The InSim host and port can be configured by copying .env to .env.local in the lfs-test directory.

yarn test:lfs

Build all example applications

This command will go through each application in the examples/ folder, install its dependencies, then build the application (typescript only).

yarn test:examples

Lint code

yarn lint

Format code

yarn format

Production build

Compiled files will be created in dist/.

yarn build

Run all checks at once

You can run code format, lint + fix, build and test with the following command:

yarn check-all

Releases

This project uses Changesets to manage publishing and release notes.

Making changes

Before committing a bug fix, a new feature or a breaking change, run the following command to create a changeset:

yarn changeset
  1. When prompted, choose if it's a patch, minor or a major change.
  2. Enter a summary for the change which will appear in the changelogs.
  3. When confirmed, a changeset Markdown file will be created in the .changeset directory.
  4. Commit the new changeset file along with your source code changes.

Releasing a new version

To bump the package version:

  1. Go to the GitHub Actions page of this repo.
  2. Select the Release workflow.
  3. Click "Run workflow".

This will bump Node InSim version according to the types of changes in the changesets and create a pull request. You can review the changes and merge the pull request once it's ready.

Once the version has been bumped, trigger the Release workflow again, which will publish a new version to NPM.


Node Insim - An open source project by Sim Broadcasts

About

A Node.js library for Live For Speed InSim protocol

Topics

Resources

Stars

16 stars

Watchers

3 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

634 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Node InSim

NPM VersionNode.js CI

An InSim library for Node.js with TypeScript support.

Introduction

Node InSim provides a JavaScript API to communicate with the Live for Speed InSim protocol over a TCP connection. After connecting to an LFS host via a hostname and a port, you are able to send InSim packets to the host and receive incoming packets from the host.

All packet structures in Node InSim are identical to the structs defined in the InSim protocol. All packet classes with all their properties are documented according to the specification.

InSim compatibility

Node InSim is compatible with InSim version 10.

Installation

Install the node-insim NPM package in your Node.js application:

npm install --save node-insim

or if you use Yarn:

yarn add node-insim

Documentation

For more detailed documentation of the public API, see https://simbroadcasts.github.io/node-insim/.

Usage

Connecting

To connect to an LFS host, you must enter its hostname, a port and a short name of your InSim application.

The InSim port must be configured in the LFS host settings. Also, make sure the public IP address from which your application is connecting is allowed to connect to the host's InSim port.

Single host

import{InSim}from'node-insim';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});

Multiple hosts

To connect to multiple hosts at once, create a new InSim instance for each host.

import{InSim}from'node-insim';constinSim1=newInSim();inSim1.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constinSim2=newInSim();inSim2.connect({Host: '127.0.0.2',Port: 30000,IName: 'Node InSim App',});

TCP / UDP

By default, Node InSim opens a TCP connection. If you want to use UDP, set the Protocol option to UDP in the connect function.

import{InSim}from'node-insim';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',Protocol: 'UDP',});

Sending packets

InSim packets can be sent using the send() method on the InSim class instance, which takes a single argument - the packet class instance.

A fast way to set packet properties is to populate them in the class constructor:

import{InSim}from'node-insim';import{IS_TINY,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.send(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_PING,}),);

Another way is to assign each property after creating the instance:

import{InSim}from'node-insim';import{IS_TINY,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constpingPacket=newIS_TINY();pingPacket.ReqI=1;pingPacket.SubT=TinyType.TINY_PING;inSim.send(pingPacket);

Waiting for packets

There are cases when you want to send a packet and then wait for a response in another packet. There is a helper method sendAwait() which waits for a given packet type and when it's received, it's resolved as a Promise. It also makes sure that the received packet's ReqI property matches the one entered in the sent packet.

import{InSim}from'node-insim';import{IS_TINY,PacketType,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.sendAwait(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_SST,}),PacketType.ISP_STA,).then((packet)=>{console.log(packet.NumConns);});

You can filter the received packet by its data using a callback in the 3rd argument:

import{InSim}from'node-insim';import{IS_TINY,PacketType,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.sendAwait(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_GTP,}),PacketType.ISP_SMALL,({ SubT })=>SubT===SmallType.SMALL_RTP,).then((packet)=>{console.log('session time',packet.UVal);});

Request-reply packet pairs

NameRequest packetReply packet
PingIS_TINY
SubT: TinyType.TINY_PING
IS_TINY
SubT: TinyType.TINY_REPLY
Session timeIS_TINY
SubT: TinyType.TINY_GTP
IS_SMALL
SubT: SmallType.SMALL_RTP
StateIS_TINY
SubT: TinyType.TINY_SST
IS_STA
InSim multiTINY_ISMIS_ISM

Sending messages

The InSim class has helper methods useful for sending messages to LFS.

Send a message which will appear on the local computer only

inSim.sendLocalMessage('Local message');

Send a command

inSim.sendMessage('/end');

Send a message

  • up to 63 characters - send an IS_MST packet
  • 64 characters or more - send an IS_MSX packet
inSim.sendMessage('This is a message');

Send a message to a specific connection by their UCID

inSim.sendMessageToConnection(4,'This is a message targeting UCID 4');

Send a message to a specific player by their PLID

inSim.sendMessageToPlayer(4,'This is a message targeting PLID 4');

Receiving packets

The InSim class exposes an on() method, which is used to listen for incoming packets by their type.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>){console.log(`Connected to LFS ${packet.Product}${packet.Version}`);}

The event callback contains the received packet, and an optional second argument - the InSim instance which received that packet. You can use that instance to send additional packets in response.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType,TinyType}from'node-insim/packets';importtype{IS_TINY}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>,inSim: InSim,){inSim.send(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_PING,}),);}

Multiple hosts

You can use the inSim argument in the event handler callback to identify the source host of the received packets, for instance by the options.Host property.

Alternatively, the InSim class constructor accepts an optional id argument, which can also be used to tell apart the InSim connections.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType}from'node-insim/packets';constinSim1=newInSim('Host One');inSim1.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constinSim2=newInSim('Host Two');inSim2.connect({Host: '127.0.0.2',Port: 30000,IName: 'Node InSim App',});inSim1.on(PacketType.ISP_VER,onVersion);inSim2.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>,inSim: InSim,){console.log(`Connected to ${inSim.options.Host}:${inSim.options.Port}`);if(inSim.id){console.log(`InSim connection ID: ${inSim.id}`);}}

String encoding

All strings in received or sent packets are automatically converted from LFS encoding to Unicode and vice versa.

If you need to access the raw LFS-encoded string in a received packet, use the _raw property in the packet instance, which contains all unconverted string properties.

import{InSim}from'node-insim';import{PacketType}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_ISM,(packet)=>{console.log(packet.HName);// UTF-8 string - ^1Drifter Team ^7★ Serverconsole.log(packet._raw.HName);// raw string - ^1Drifter Team ^7^J�� Server\u0000\u0000\u0000\u0000});

When you send a Unicode string value in a packet, each character will get encoded into the correct LFS encoding, so LFS can display the text in a message or a button.

import{InSim}from'node-insim';import{PacketType}from'node-insim/packets';importtype{IS_MSL}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,(packet)=>{inSim.send(newIS_MSL({Msg: 'čau světe',// LFS will receive: ^Eèau svìte}),);});

Special characters

Special care needs to be taken when sending strings containing caret (^) and slash (/) characters:

  • A caret needs to be escaped as ^^ because the caret symbol acts as an escape character in LFS. Sending ^^hello as a message will display ^hello in LFS. Sending just ^hello would appear as #ello.
  • A slash needs to be escaped as ^s, otherwise it is treated as an LFS in-game command prefix when used in a message packet. Sending ^sjoin as a message will display /join in LFS, whereas sending /join will make the player join the track.

OutGauge

import{OutGauge}from'node-insim';constoutGauge=newOutGauge();outGauge.connect({Host: '127.0.0.1',Port: 29999,});outGauge.on('packet',(data)=>{console.clear();console.log(data.RPM);});

OutSim

import{OutSim,OutSimPack}from'node-insim';constoutSim=newOutSim();outSim.connect({Host: '127.0.0.1',Port: 29999,});outSim.on('packet',(data)=>{// Make sure the simple OutSimPack packet is really received, as opposed to OutSimPack2if(!(datainstanceofOutSimPack)){return;}console.clear();console.log(data.PosX);});

Debugging

Node InSim uses the debug NPM package for debug logs. By default, Node InSim does not output any logs to the standard output.

To enable logging, use the DEBUG environment variable when running your InSim application. All logs are prefixed with node-insim. You can use wildcards to filter out the logs that you need.

DEBUG=* node insim.js # debug all messages
DEBUG=node-insim:tcp node insim.js # debug only TCP protocol messages

Example applications

You can find example applications using Node InSim in the examples folder.

Example
InSim connectionJavaScript + CJSTypeScript + ESM
InSim connection (multiple hosts)JavaScript + CJSTypeScript + ESM
InSim connection (UDP)JavaScript + CJSTypeScript + ESM
OutGaugeJavaScript + CJSTypeScript + ESM
OutGauge with InSim buttonsJavaScript + CJSTypeScript + ESM
OutSimJavaScript + CJSTypeScript + ESM
OutSim with OptionsJavaScript + CJSTypeScript + ESM

Before you run an example, follow the instructions in each example's README.md file.

For instance, to run the "InSim connection - TypeScript" example, run the following commands:

cd examples/typescript/insim-connection
npm install
npm start

Development

Requirements

  • Node.js 18
  • Yarn

Start a development server

yarn dev

Code generators

When adding new InSim packets to the library, you can use built-in code generators using yarn generate. It will create and update all the necessary files for you.

Run unit tests

yarn test

Run tests against a real LFS application

To run these tests, LFS must be running with an InSim port open.

By default, the tests connect to 127.0.0.1:29999. The InSim host and port can be configured by copying .env to .env.local in the lfs-test directory.

yarn test:lfs

Build all example applications

This command will go through each application in the examples/ folder, install its dependencies, then build the application (typescript only).

yarn test:examples

Lint code

yarn lint

Format code

yarn format

Production build

Compiled files will be created in dist/.

yarn build

Run all checks at once

You can run code format, lint + fix, build and test with the following command:

yarn check-all

Releases

This project uses Changesets to manage publishing and release notes.

Making changes

Before committing a bug fix, a new feature or a breaking change, run the following command to create a changeset:

yarn changeset
  1. When prompted, choose if it's a patch, minor or a major change.
  2. Enter a summary for the change which will appear in the changelogs.
  3. When confirmed, a changeset Markdown file will be created in the .changeset directory.
  4. Commit the new changeset file along with your source code changes.

Releasing a new version

To bump the package version:

  1. Go to the GitHub Actions page of this repo.
  2. Select the Release workflow.
  3. Click "Run workflow".

This will bump Node InSim version according to the types of changes in the changesets and create a pull request. You can review the changes and merge the pull request once it's ready.

Once the version has been bumped, trigger the Release workflow again, which will publish a new version to NPM.


Node Insim - An open source project by Sim Broadcasts

About

A Node.js library for Live For Speed InSim protocol

Topics

Resources

Stars

16 stars

Watchers

3 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

634 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Node InSim

NPM VersionNode.js CI

An InSim library for Node.js with TypeScript support.

Introduction

Node InSim provides a JavaScript API to communicate with the Live for Speed InSim protocol over a TCP connection. After connecting to an LFS host via a hostname and a port, you are able to send InSim packets to the host and receive incoming packets from the host.

All packet structures in Node InSim are identical to the structs defined in the InSim protocol. All packet classes with all their properties are documented according to the specification.

InSim compatibility

Node InSim is compatible with InSim version 10.

Installation

Install the node-insim NPM package in your Node.js application:

npm install --save node-insim

or if you use Yarn:

yarn add node-insim

Documentation

For more detailed documentation of the public API, see https://simbroadcasts.github.io/node-insim/.

Usage

Connecting

To connect to an LFS host, you must enter its hostname, a port and a short name of your InSim application.

The InSim port must be configured in the LFS host settings. Also, make sure the public IP address from which your application is connecting is allowed to connect to the host's InSim port.

Single host

import{InSim}from'node-insim';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});

Multiple hosts

To connect to multiple hosts at once, create a new InSim instance for each host.

import{InSim}from'node-insim';constinSim1=newInSim();inSim1.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constinSim2=newInSim();inSim2.connect({Host: '127.0.0.2',Port: 30000,IName: 'Node InSim App',});

TCP / UDP

By default, Node InSim opens a TCP connection. If you want to use UDP, set the Protocol option to UDP in the connect function.

import{InSim}from'node-insim';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',Protocol: 'UDP',});

Sending packets

InSim packets can be sent using the send() method on the InSim class instance, which takes a single argument - the packet class instance.

A fast way to set packet properties is to populate them in the class constructor:

import{InSim}from'node-insim';import{IS_TINY,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.send(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_PING,}),);

Another way is to assign each property after creating the instance:

import{InSim}from'node-insim';import{IS_TINY,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constpingPacket=newIS_TINY();pingPacket.ReqI=1;pingPacket.SubT=TinyType.TINY_PING;inSim.send(pingPacket);

Waiting for packets

There are cases when you want to send a packet and then wait for a response in another packet. There is a helper method sendAwait() which waits for a given packet type and when it's received, it's resolved as a Promise. It also makes sure that the received packet's ReqI property matches the one entered in the sent packet.

import{InSim}from'node-insim';import{IS_TINY,PacketType,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.sendAwait(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_SST,}),PacketType.ISP_STA,).then((packet)=>{console.log(packet.NumConns);});

You can filter the received packet by its data using a callback in the 3rd argument:

import{InSim}from'node-insim';import{IS_TINY,PacketType,TinyType}from'node-insim/packets';constinSim=newInSim();inSim.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});inSim.sendAwait(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_GTP,}),PacketType.ISP_SMALL,({ SubT })=>SubT===SmallType.SMALL_RTP,).then((packet)=>{console.log('session time',packet.UVal);});

Request-reply packet pairs

NameRequest packetReply packet
PingIS_TINY
SubT: TinyType.TINY_PING
IS_TINY
SubT: TinyType.TINY_REPLY
Session timeIS_TINY
SubT: TinyType.TINY_GTP
IS_SMALL
SubT: SmallType.SMALL_RTP
StateIS_TINY
SubT: TinyType.TINY_SST
IS_STA
InSim multiTINY_ISMIS_ISM

Sending messages

The InSim class has helper methods useful for sending messages to LFS.

Send a message which will appear on the local computer only

inSim.sendLocalMessage('Local message');

Send a command

inSim.sendMessage('/end');

Send a message

  • up to 63 characters - send an IS_MST packet
  • 64 characters or more - send an IS_MSX packet
inSim.sendMessage('This is a message');

Send a message to a specific connection by their UCID

inSim.sendMessageToConnection(4,'This is a message targeting UCID 4');

Send a message to a specific player by their PLID

inSim.sendMessageToPlayer(4,'This is a message targeting PLID 4');

Receiving packets

The InSim class exposes an on() method, which is used to listen for incoming packets by their type.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>){console.log(`Connected to LFS ${packet.Product}${packet.Version}`);}

The event callback contains the received packet, and an optional second argument - the InSim instance which received that packet. You can use that instance to send additional packets in response.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType,TinyType}from'node-insim/packets';importtype{IS_TINY}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>,inSim: InSim,){inSim.send(newIS_TINY({ReqI: 1,SubT: TinyType.TINY_PING,}),);}

Multiple hosts

You can use the inSim argument in the event handler callback to identify the source host of the received packets, for instance by the options.Host property.

Alternatively, the InSim class constructor accepts an optional id argument, which can also be used to tell apart the InSim connections.

import{InSim}from'node-insim';import{InSimPacketInstance,PacketType}from'node-insim/packets';constinSim1=newInSim('Host One');inSim1.connect({Host: '127.0.0.1',Port: 29999,IName: 'Node InSim App',});constinSim2=newInSim('Host Two');inSim2.connect({Host: '127.0.0.2',Port: 30000,IName: 'Node InSim App',});inSim1.on(PacketType.ISP_VER,onVersion);inSim2.on(PacketType.ISP_VER,onVersion);functiononVersion(packet: InSimPacketInstance<PacketType.ISP_VER>,inSim: InSim,){console.log(`Connected to ${inSim.options.Host}:${inSim.options.Port}`);if(inSim.id){console.log(`InSim connection ID: ${inSim.id}`);}}

String encoding

All strings in received or sent packets are automatically converted from LFS encoding to Unicode and vice versa.

If you need to access the raw LFS-encoded string in a received packet, use the _raw property in the packet instance, which contains all unconverted string properties.

import{InSim}from'node-insim';import{PacketType}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_ISM,(packet)=>{console.log(packet.HName);// UTF-8 string - ^1Drifter Team ^7★ Serverconsole.log(packet._raw.HName);// raw string - ^1Drifter Team ^7^J�� Server\u0000\u0000\u0000\u0000});

When you send a Unicode string value in a packet, each character will get encoded into the correct LFS encoding, so LFS can display the text in a message or a button.

import{InSim}from'node-insim';import{PacketType}from'node-insim/packets';importtype{IS_MSL}from'node-insim/packets';constinSim=newInSim();inSim.on(PacketType.ISP_VER,(packet)=>{inSim.send(newIS_MSL({Msg: 'čau světe',// LFS will receive: ^Eèau svìte}),);});

Special characters

Special care needs to be taken when sending strings containing caret (^) and slash (/) characters:

  • A caret needs to be escaped as ^^ because the caret symbol acts as an escape character in LFS. Sending ^^hello as a message will display ^hello in LFS. Sending just ^hello would appear as #ello.
  • A slash needs to be escaped as ^s, otherwise it is treated as an LFS in-game command prefix when used in a message packet. Sending ^sjoin as a message will display /join in LFS, whereas sending /join will make the player join the track.

OutGauge

import{OutGauge}from'node-insim';constoutGauge=newOutGauge();outGauge.connect({Host: '127.0.0.1',Port: 29999,});outGauge.on('packet',(data)=>{console.clear();console.log(data.RPM);});

OutSim

import{OutSim,OutSimPack}from'node-insim';constoutSim=newOutSim();outSim.connect({Host: '127.0.0.1',Port: 29999,});outSim.on('packet',(data)=>{// Make sure the simple OutSimPack packet is really received, as opposed to OutSimPack2if(!(datainstanceofOutSimPack)){return;}console.clear();console.log(data.PosX);});

Debugging

Node InSim uses the debug NPM package for debug logs. By default, Node InSim does not output any logs to the standard output.

To enable logging, use the DEBUG environment variable when running your InSim application. All logs are prefixed with node-insim. You can use wildcards to filter out the logs that you need.

DEBUG=* node insim.js # debug all messages
DEBUG=node-insim:tcp node insim.js # debug only TCP protocol messages

Example applications

You can find example applications using Node InSim in the examples folder.

Example
InSim connectionJavaScript + CJSTypeScript + ESM
InSim connection (multiple hosts)JavaScript + CJSTypeScript + ESM
InSim connection (UDP)JavaScript + CJSTypeScript + ESM
OutGaugeJavaScript + CJSTypeScript + ESM
OutGauge with InSim buttonsJavaScript + CJSTypeScript + ESM
OutSimJavaScript + CJSTypeScript + ESM
OutSim with OptionsJavaScript + CJSTypeScript + ESM

Before you run an example, follow the instructions in each example's README.md file.

For instance, to run the "InSim connection - TypeScript" example, run the following commands:

cd examples/typescript/insim-connection
npm install
npm start

Development

Requirements

  • Node.js 18
  • Yarn

Start a development server

yarn dev

Code generators

When adding new InSim packets to the library, you can use built-in code generators using yarn generate. It will create and update all the necessary files for you.

Run unit tests

yarn test

Run tests against a real LFS application

To run these tests, LFS must be running with an InSim port open.

By default, the tests connect to 127.0.0.1:29999. The InSim host and port can be configured by copying .env to .env.local in the lfs-test directory.

yarn test:lfs

Build all example applications

This command will go through each application in the examples/ folder, install its dependencies, then build the application (typescript only).

yarn test:examples

Lint code

yarn lint

Format code

yarn format

Production build

Compiled files will be created in dist/.

yarn build

Run all checks at once

You can run code format, lint + fix, build and test with the following command:

yarn check-all

Releases

This project uses Changesets to manage publishing and release notes.

Making changes

Before committing a bug fix, a new feature or a breaking change, run the following command to create a changeset:

yarn changeset
  1. When prompted, choose if it's a patch, minor or a major change.
  2. Enter a summary for the change which will appear in the changelogs.
  3. When confirmed, a changeset Markdown file will be created in the .changeset directory.
  4. Commit the new changeset file along with your source code changes.

Releasing a new version

To bump the package version:

  1. Go to the GitHub Actions page of this repo.
  2. Select the Release workflow.
  3. Click "Run workflow".

This will bump Node InSim version according to the types of changes in the changesets and create a pull request. You can review the changes and merge the pull request once it's ready.

Once the version has been bumped, trigger the Release workflow again, which will publish a new version to NPM.


Node Insim - An open source project by Sim Broadcasts

About

A Node.js library for Live For Speed InSim protocol

Topics

Resources

Stars

16 stars

Watchers

3 watching

Forks

Releases

Used by

Contributors

Languages