The online racing simulator
.NET - InSim.NET - InSim library
InSim.NET is a InSim library for Live for Speed, written for the .NET Framework. It allows you to create a socket connection with the game and to send and receive packets of data. These packets can be used to control LFS, issue commands and request information. The library has been designed for flexibility, extensibility and performance. It tries to match the original InSim API as closely as possible, while taking care not to get in your way.

Note: InSim.NET was originally known as Spark, but changed its name to prevent it clashing with another .NET project with the same name. Note: Many code examples in this thread were written for version 1.0 the library and probably won't work correctly in the newer version. You can find up-to-date examples in the documentation.

Quick LinksWhat's New?

This release is for version 2.0 of the library, which has seen a number of large changes
  • Full support for InSim, InSim Relay, OutSim and OutGauge
  • Full support for all LFS character encodings
  • Improved API
  • Improved networking code
  • Improved documentation
  • A new home on GitHub
  • New Git source code repository
Requirements

InSim.NET requires .NET Framework 4.5 and is CLS compliant with all .NET languages. It is written in C# and compiled with Visual Studio 2013.

Source Code

InSim.NET now uses Git distributed source control, which means it's easy to clone the latest repository, create forks and make changes. Anyone is welcome to clone the repository and create their own version of the library.

License

InSim.NET is released under the Lesser General Public License (LGPL).

Download

InSim.NET is available through NuGet, the .NET package manager. To install it type the following command at the package manager console.

PM> Install-Package InSimDotNet

Alternatively you can download it from the GitHub project page.

https://github.com/alexmcbride/insimdotnet
#2 - filur
Looks very nice, i'll give it a go sometime.
Quote from filur :Looks very nice, i'll give it a go sometime.

Going over to the Spark side?!

[Ok that was lame, but I did find it just a little funny.]

DarkTimes as always, love the code coming from your direction!
#4 - filur
Packets.PacketFactory.Build seems to be missing a case for ISP_BTC, a bug or am i missing something?
Obviously a bug.
I've always wanted to build my C# insim projects on this base! Now I can finally get it without the sniffer.

As soon as I have some time for it, I'll definitely check it out!
Mmm, looks easy enough to start learning some C# this summer holidays
#8 - filur
Here is Sparktris.

The patch for Spark can be found in the source zip.

The binary probably requires the .NET framework v4 or higher.

The game is single player and intended to be connected to a local LFS client.
Attached files
sparktris.zip - 56.5 KB - 1325 views
sparktris-source.zip - 80.8 KB - 1401 views
Quote from filur :Here is Sparktris.

The patch for Spark can be found in the source zip.

The binary probably requires the .NET framework v4 or higher.

The game is single player and intended to be connected to a local LFS client.

You ... Have way to much time on your hands .
[Read next post first]


This is not as easy as I thought

I'm trying the "Car Updates" example, and I found 2 problems.

1-"using Spark.Helpers;" sentence is missing

2-I execute the program and I see "InSim - TCP : Example" message in LFS, but after that nothing appear in the console


I tried first with a Windows Forms proyect, but that was too much for me. I can't even run this copy&paste example haha


EDIT: the other examples work perfect
It seems that the dictionary isn't working right (I'm new to C# and dictionaries).

In "NewPlayer" function this line "_players.Add(npl.PLID, npl);" adds a driver (PLID=20, for example)

But in "PlayerLeft" function, "_players.ContainsKey(pll.PLID)" returns false with the same PLID (this time it comes from pll packet instead of npl, but it still PLID=20).



Am I doing something wrong or that dictionary isn't working fine?

I'm pretty sure this is the cause to the problem in my previous post
Sorry, I've not had time to work on this since I posted it. Been very busy. I'll try to get all the kinks worked out over the weekend.

In terms of the car update sample, it appears I missed out a couple of things, mainly I forgot to set the MCI flag and interval in the ISI packet. I'm not at home to test this, but try this code:

using System;
using System.Collections.Generic;
using Spark;
using Spark.Packets;
using Spark.Helpers;

class Program
{
// We store the players in a dictionary.
static Dictionary<int, IS_NPL> _players = new Dictionary<int, IS_NPL>();

static void Main()
{
// Create new InSim object.
using (var insim = new InSim())
{
// Bind handlers.
insim.Bind<IS_NPL>(NewPlayer);
insim.Bind<IS_PLL>(PlayerLeft);
insim.Bind<IS_MCI>(MultiCarInfo);

// Establish the InSim connection.
insim.Connect("127.0.0.1", 29999);

// Initialize InSim.
insim.Send(new IS_ISI { IName = "^3Example", Flags = InSimFlags.ISF_MCI, Interval = 500 });

// Request players.
insim.Send(new IS_TINY { SubT = TinyType.TINY_NPL, ReqI = 255 });

// Prevent program from exiting.
insim.Run();
}
}

static void NewPlayer(IS_NPL npl)
{
if (_players.ContainsKey(npl.PLID))
{
// Leaving pits, just update NPL object.
_players[npl.PLID] = npl;
}
else
{
// Add new player.
_players.Add(npl.PLID, npl);
}
}

static void PlayerLeft(IS_PLL pll)
{
// Remove player.
_players.Remove(pll.PLID);
}

static void MultiCarInfo(IS_MCI mci)
{
// Loop through each car on track.
foreach (var car in mci.CompCars)
{
IS_NPL npl;
if (_players.TryGetValue(car.PLID, out npl))
{
// Convert LFS speed into Mph.
var mph = MathHelper.SpeedToMph(car.Speed);

// Print nicely formatted string to console.
Console.WriteLine("Speed: {0} {1:F2}", npl.PName, mph);
}
}
}
}

OK - Thanks for those!

I've uploaded a new version to the first post, with those fixes, as well as a couple of other things. I created a separate solution with all the examples in it, so it's easier for people to get off the ground. You can find it in the SparkExamples folder. I also added a readme and proper licenses.

In case I didn't mention it before the library is released under the LGPL.
Quote from DarkTimes :I've uploaded a new version to the first post, with those fixes

Had a quick look and noticed this didn't make it into IS_BTN.cs:
- writer.Write(Text, 240);
+ writer.Write(Text, Size - 12);

Ah sorry, trying to do things too fast. I've uploaded a fixed version.

As I say I'll try to do real work on it this weekend.
Had another look and spotted two issues in IS_BTN.cs where a text longer than 240 chars would be handled incorrectly and the variable size stuff padded an empty text to a length of 4.

So here's a patch for those.
Attached files
spark_1.0.1-is_btn_fix.patch.txt - 533 B - 780 views
Quote from filur :Had another look and spotted two issues in IS_BTN.cs where a text longer than 240 chars would be handled incorrectly and the variable size stuff padded an empty text to a length of 4.

So here's a patch for those.

Fantastic! Good to see filur programming again!
It's receiving MCI packets

I'm still tryng to understand the basis of this, as I wasn't able yet to use the dictionary or get the license name of a player (me xD)



Another bug:
These two functions return the same speed. They return my speed in kph

var mph = MathHelper.SpeedToMph(car.Speed);
var kph = MathHelper.SpeedToKph(car.Speed);

Wierd enough my dictionary has started to work correctly itself
The dictionary should work, dunno what problems you're having.

There is a bug in the double MathHelper.SpeedToMph(int) method, I will release a fix at some point, but you can change it to this:

public static double SpeedToMph(int speed)
{
return speed / 146.486067;
}

Obviously there are several issues that need to be worked out in the library, my plan is to port InSimSniffer over to Spark which should let me resolve most of these problems.

I realise I also need to add proper string decoding, but I've been considering releasing a standalone library for this.
Well actually an issue which could affect the dictionary of players is if you connect to LFS on the menu screen and then join a host. A better solution would be to request the players both when connecting to LFS and also when joining a multiplayer race.

using System;
using System.Collections.Generic;
using Spark;
using Spark.Packets;
using Spark.Helpers;

namespace Spark.Example5
{
class Program
{
static InSim _insim;
// We store the players in a dictionary.
static Dictionary<int, IS_NPL> _players = new Dictionary<int, IS_NPL>();

static void Main()
{
// Create new InSim object.
using (_insim = new InSim())
{
// Bind handlers.
_insim.Bind<IS_ISM>(MultiPlayerInfo);
_insim.Bind<IS_NPL>(NewPlayer);
_insim.Bind<IS_PLL>(PlayerLeft);
_insim.Bind<IS_MCI>(MultiCarInfo);

// Establish the InSim connection.
_insim.Connect("127.0.0.1", 29999);

// Initialize InSim.
_insim.Send(new IS_ISI { IName = "^3Example", Flags = InSimFlags.ISF_MCI, Interval = 500, Admin = string.Empty });

// Request for multiplayer info packet to be sent.
_insim.Send(new IS_TINY { SubT = TinyType.TINY_ISM, ReqI = 255 });

// Prevent program from exiting.
_insim.Run();
}
}

static void MultiPlayerInfo(IS_ISM ism)
{
// When joining multiplayer request for all connections and players to be sent.
_insim.Send(new IS_TINY { SubT = TinyType.TINY_NCN, ReqI = 255 });
_insim.Send(new IS_TINY { SubT = TinyType.TINY_NPL, ReqI = 255 });
}

static void NewPlayer(IS_NPL npl)
{
if (_players.ContainsKey(npl.PLID))
{
// Leaving pits, just update NPL object.
_players[npl.PLID] = npl;
}
else
{
// Add new player.
_players.Add(npl.PLID, npl);
}
}

static void PlayerLeft(IS_PLL pll)
{
// Remove player.
_players.Remove(pll.PLID);
}

static void MultiCarInfo(IS_MCI mci)
{
// Loop through each car on track.
foreach (var car in mci.CompCars)
{
// Get the player driving this car.
IS_NPL npl;
if (_players.TryGetValue(car.PLID, out npl))
{
// Check cars speed.
var kph = MathHelper.SpeedToKph(car.Speed);
if (kph > 80)
{
// Spectate player and send chat message.
_insim.Send("/spec {0}", npl.PName);
_insim.Send("{0} ^3spectated for speeding", npl.PName);
}
}
}
}
}
}

Quote from DarkTimes :I realise I also need to add proper string decoding, but I've been considering releasing a standalone library for this.

Yes me too. with the LFSWorld SDK and PRISM both having to decode strings and parse times, and convert speed I thought I might as well release a standalone lib to handle all such functions. As it is going to be used by many projects the license for such a lib would have to be very liberal, to the point where it would have to be MIT. Nothing else really comes close as far as code portability.
Uploaded 1.0.2 to the first post.

Changes Since 1.0.1
  • Allowed reuse of InSim, TcpSocket and UdpSocket without needing to create a new instance each time
  • Added asyncronous connect, by calling InSim.BeginConnect(string, int, AsyncCallback) and InSim.EndConnect(IAsyncResult) methods
  • Added InSim.Connecting event which is raised when the connect operation is begun but before the connection is established
  • InSim.Connect and InSim.EndConnect try to throw an appropriate InSimException instead of a generic SocketException
  • Added IS_BTN patch supplied by filur
  • Fixed bug with MathHelper.SpeedToMph method where it was returning Kph
  • Fixed "existing connection closed by remote host" error when calling InSim.Disconnect()
  • Lots of small corrections and tweaks to the SparkExamples solution
It's nice to see you have time again to improve this

FGED GREDG RDFGDR GSFDG