The online racing simulator
Hey, found a little issue with this lib.

Basically I have a Connection list, which has variables, like

public string Username;
public string PlayerName;

So I am collecting info from MCI, and I have made a CompCar variable like so:

public CompCar Car;

When trying to put info from MCI onto this, I get the usual "Object reference not set to an instance of an object" error... so then I tried this:

public CompCar Car = new CompCar();

This pulled up the error : "The type 'Spark.Packets.CompCar' has no constructors defined".

So I was unable to reference the code, so I ended up writing my own structure:

public CarPack Car = new CarPack();

public struct CarPack
{
public short AngVel;
public ushort Direction;
public ushort Heading;
public CompCarFlags Info;
public ushort Lap;
public ushort Node;
public ushort Speed;
public byte PLID;
public byte Position;
public int X;
public int Y;
public int Z;
}

This code solved it.

Nevertheless I think you should define the "constructors" in future updates, to help other coming from LFS_External
What is it you are actually trying to do? I don't understand what the problem is. CompCar doesn't have a public constructor because there is no reason for you to ever construct it, that's handled internally by the library. You will need to give more details about what you are trying to do and what problem you're having. Almost certainly it sounds like you are trying to do something in a strange way.

Edit: If you are getting a NullReferenceException then you need to check that a reference is not null before you use it

if (car != null)
{
// Do something with CompCar.
}

@PoVo: Just set it to null (if I'm guessing right on what you're trying to do that is)?
Quote from broken :@PoVo: Just set it to null (if I'm guessing right on what you're trying to do that is)?

That won't work, it will pull upthe same error.

I was trying to put a CompCar variable into my clsConnection class which specifies variables for each connection.

When the insim app received an MCI packet, I would "keep" the info on the CompCar var that I set up in the clsCon. class. E.g:

Conn.Car.Speed = car.Speed;

Once I used this code, the unreference error appeared meaning I had to add a " = new CompCar();"

Hope you understand it ;D
A part of my clsUser.cs :

private CompCar _compcar = null;
public CompCar CompCar
{
get { return _compcar; }
set { _compcar = value; }
}

That's working without a problem.

In my MCI handler, I just do (rough code): User.CompCar = MCI.CompCar.

[E] Real MCI handler code:
private void Received_MCI(IS_MCI MCI)
{
int NumC = MCI.NumC;

for (int i = 0; i < NumC; i++)
{
CompCar CC = MCI.CompCars[i];
clsUser U = Users[getUserIdxByPLID(CC.PLID)];



U.CompCar = CC;
}
}

Yeah, I've given it a bit of LFS_External look, because that does the trick for me. ;d

Hope that helps.
That doesn't work either

Edit: Solved it like this:

public CompCar Car;

foreach (var car in MCI.CompCars)
{
IS_NPL npl;
if (_players.TryGetValue(car.PLID, out npl))
{
int idx = 0;
idx = ConnByPLID(car.PLID);
var Conn = Connections[idx];

Conn.Car = car; // Varies the variable with the whole structure instead of varying each variable in the structure.

MCI_Update(car.PLID); // Updates some information
}
}

Where is the NullReferenceException being thrown in your code? That's where the actual problem is, all this stuff about CompCar is just getting in the way. You said in your first post:

Quote from PoVo :When trying to put info from MCI onto this, I get the usual "Object reference not set to an instance of an object" error... so then I tried this:

It sounds to me like this is the actual problem we need to solve. I'm sorry, but I need to see more code. I'm not trying to be funny, but I do really believe that you're making the solution to this more complicated that it needs to be.
Quote from DarkTimes :Where is the NullReferenceException being thrown in your code? That's where the actual problem is, all this stuff about CompCar is just getting in the way. You said in your first post:



It sounds to me like this is the actual problem we need to solve. I'm sorry, but I need to see more code. I'm not trying to be funny, but I do really believe that you're making the solution to this more complicated that it needs to be.

The error was thrown at the line:

Conn.CompCar.Speed = car.Speed;

or any other line like :

Conn.CompCar.AngVel = car.AngVel;

So the fault was that "public CompCar CompCar;" wasn't referenced.
OK here is a quick example I've written that shows saving the CompCar object in a connection object. It's a simple app that prints out the users location when the type the command '!location'.

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

namespace ConsoleApplication4
{
class Program
{
// Class to store connection info.
class Connection
{
public CompCar Car { get; set; }
}

static Dictionary<int, IS_NPL> players = new Dictionary<int, IS_NPL>();
static Dictionary<int, Connection> connections = new Dictionary<int, Connection>();

static void Main(string[] args)
{
using (InSim insim = new InSim())
{
insim.Bind<IS_ISM>(InSimMulti);
insim.Bind<IS_NCN>(NewConnection);
insim.Bind<IS_NPL>(NewPlayer);
insim.Bind<IS_MCI>(MultiCarInfo);
insim.Bind<IS_MSO>(MessageOut);

// Connect to InSim.
insim.Connect("127.0.0.1", 29999);
insim.Send(new IS_ISI
{
Prefix = '!',
Interval = 1000,
Flags = InSimFlags.ISF_MCI,
});

// Request host packet.
insim.Send(new IS_TINY { ReqI = 1, SubT = TinyType.TINY_ISM });

Console.WriteLine("Press any key to exit...");
Console.ReadKey(true);
}
}

static void InSimMulti(InSim insim, IS_ISM ism)
{
// Whenever we connect to a host request the player and connection lists.
insim.Send(new IS_TINY { ReqI = 1, SubT = TinyType.TINY_NCN });
insim.Send(new IS_TINY { ReqI = 1, SubT = TinyType.TINY_NPL });
}

static void NewPlayer(InSim insim, IS_NPL npl)
{
// Add new player.
players[npl.PLID] = npl;
}

static void NewConnection(InSim insim, IS_NCN ncn)
{
// Add new connection.
connections[ncn.UCID] = new Connection();
}

static void MultiCarInfo(InSim insim, IS_MCI mci)
{
foreach (CompCar car in mci.CompCars)
{
IS_NPL npl;
Connection conn;
if (players.TryGetValue(car.PLID, out npl) &&
connections.TryGetValue(npl.UCID, out conn))
{
// Update car on connection.
conn.Car = car;
}
}
}

static void MessageOut(InSim insim, IS_MSO mso)
{
if (mso.UserType == UserType.MSO_PREFIX)
{
// Parse command.
string args = mso.Msg.Substring(mso.TextStart);
if (args.Equals("!location", StringComparison.InvariantCultureIgnoreCase))
{
Connection conn = connections[mso.UCID];

// Check we have received a compcar for this connection yet.
if (conn.Car != null)
{
// Send location message.
string message = String.Format(
"X: {0:F2} Y: {1:F2} Z: {2:F2}",
MathHelper.LengthToMeters(conn.Car.X),
MathHelper.LengthToMeters(conn.Car.Y),
MathHelper.LengthToMeters(conn.Car.Z));

insim.Send(message, mso.UCID);
}
}
}
}
}
}

Quote from DarkTimes :[post above]

Isn't this the same thing I did?

Just copy the whole CompCar, and not the values one by one. <-- Think that's the problem(or ..solution), since the errors are thrown on these specific lines.
Quote from broken :Isn't this the same thing I did?

Just copy the whole CompCar, and not the values one by one. <-- Think that's the problem(or ..solution), since the errors are thrown on these specific lines.

Basically yes, the same thing as my solution is used on the Example.
Quote from PoVo :Basically yes, the same thing as my solution is used on the Example.

The purpose of my example was to show that what you are trying to do is possible without making the constructor for CompCar public. If you really wanna make CompCar constructable then you can edit the CompCar.cs class to add a blank public constructor and recompile the library.

But as I showed in my example you don't need to do this to be able to store a reference to the CompCar for access later, it currently works as it is.

Edit: The reason that CompCar has an internal constructor instead of a public one is because it requires a parameter that's an internal data-structure called PacketReader, which is not available outside of the assembly (for complicated reasons). At the time it was written I considered providing an alternative public constructor, but realized that if you are trying to create an instance of CompCar then either you are doing something wrong or something the library was never designed to do (in which case the library should be redesigned). The reason that you don't need to worry about constructors when using LFS_External is because all packets in that library are structs and not classes, so automatically provide default constructors like all value-types. However the reason that all packets are structs in LFS_External is an implementation detail and was not made because of a design decision.
I have released InSim.NET 2.0.0 Beta to CodePlex project site. The download link can be found here:

http://insimdotnet.codeplex.com/releases/

Features in this release
  • Full InSim, InSim Relay, OutSim and OutGauge support
  • Better support for .NET languages other than C#
  • Improvements to string encoding/decoding
  • General API improvements
  • Mercurial source control repository
Changes

There are quite a few changes in this release, although most stuff will be familiar to people who have used to library before. There are too many changes to recount from the top of my head but I'll try to give the bullet-points.

InSim

Firstly the biggest change you'll notice is that you now initialize the InSim connection differently. To initialize the connection you now use the new InSim.Initialize(InSimSettings) method. Like so:

using System;
using InSimDotNet;

class Program
{
static void Main()
{
// Create new InSim object.
using (InSim insim = new InSim())
{
// Initialize InSim.
insim.Initialize(new InSimSettings
{
Host = "127.0.0.1",
Port = 29999,
Admin = String.Empty,
});

// Send message 'Hello, InSim!' to the game chat.
insim.Send("/msg Hello, InSim!");

// Stop program from exiting.
Console.WriteLine("Press any key to exit...");
Console.ReadKey(true);
}
}
}

Also the way in which you bind packets has changed slightly, as you now must always specify a InSim parameter on the packet handler. Like this:

using System;
using InSimDotNet;
using InSimDotNet.Packets;

class Program
{
static void Main()
{
using (InSim insim = new InSim())
{
// Bind packet handler.
insim.Bind<IS_MSO>(MessageOut);

insim.Initialize(new InSimSettings
{
Host = "127.0.0.1",
Port = 29999,
Admin = String.Empty,
});

// Stop program from exiting.
Console.WriteLine("Press any key to exit...");
Console.ReadKey(true);
}
}

// Packet handler now must specify a InSim parameter.
static void MessageOut(InSim insim, IS_MSO mso)
{
Console.WriteLine(mso.Msg);
}
}

With the changes to the way you initialize InSim, you can now specify a separate UDP port for MCI and NLP updates.

using System;
using InSimDotNet;
using InSimDotNet.Packets;

class Program
{
static void Main()
{
using (InSim insim = new InSim())
{
insim.Bind<IS_MCI>(MultiCarInfo);

insim.Initialize(new InSimSettings
{
Host = "127.0.0.1",
Port = 29999,
Admin = String.Empty,
UdpPort = 30000, // Specify UDP port
Interval = 1000, // Update interval (milliseconds)
Flags = InSimFlags.ISF_MCI, // Enable MCI updates
});

// Stop program from exiting.
Console.WriteLine("Press any key to exit...");
Console.ReadKey(true);
}
}

// Handle MCI packet.
static void MultiCarInfo(InSim insim, IS_MCI mci)
{
foreach (CompCar car in mci.Info)
{
// Do something with CompCar.
}
}
}

InSim Relay

InSim relay works exactly as before, except now you initialize it by setting the InSimSettings.IsRelayHost property to true.

using System;
using InSimDotNet;

class Program
{
static void Main()
{
using (InSim insim = new InSim())
{
insim.Initialize(new InSimSettings
{
IsRelayHost = true, // Initialize InSim Relay.
});

// Stop program from exiting.
Console.WriteLine("Press any key to exit...");
Console.ReadKey(true);
}
}
}

When IsRelayHost is set all other settings are ignored.

OutGauge and OutSim

There is now full OutSim and OutGauge support. Here is an example of using OutSim.

using System;
using InSimDotNet.Out;

class Program
{
static void Main()
{
// Create new OutSim object.
using (OutSim outsim = new OutSim())
{
// Bind OutSim packet event.
outsim.PacketReceived += new EventHandler<OutSimEventArgs>(outsim_PacketReceived);

// Start listening for packets sent from LFS.
outsim.Connect("127.0.0.1", 30000);

// Stop program from exiting.
Console.WriteLine("Press any key to exit...");
Console.ReadKey(true);
}
}

static void outsim_PacketReceived(object sender, OutSimEventArgs e)
{
// Do something with packet.
OutSimPack packet = e.Packet;
}
}

Download

You can download the InSim.NET 2.0.0 Beta from CodePlex right now!
As you'll notice the old InSim.Run() method has been removed, as it was just confusing to most people. You now need to keep the application open yourself, which there are several ways to handle.

Firstly you can do it simply, like in my examples above.

Console.WriteLine("Press any key to exit...");

// Block until the user presses a key.
Console.ReadKey(true);

With this method it's easy to close the application by mistake, so it would be better to use a specific key combination. Here's an example of how you would do that.

Console.WriteLine("Press Ctrl+Z to exit...");

// Poll for the correct key combination.
ConsoleKeyInfo key;
do
{
key = Console.ReadKey(true);
}
while (!key.Modifiers.HasFlag(ConsoleModifiers.Control) && key.Key != ConsoleKey.Z);

Alternatively you can have the program stay open until the connection with LFS has been closed.

while (insim.IsConnected)
{
Thread.Sleep(200);
}

Of course if you're using the library from a GUI application then the program will stay open automatically.
I've also created a simple help file with the documentation, that you can download separably. It's not perfect, a couple of small things are missing, but it works reasonably well.

Edit: help file removed, it is now a part of the official download.
Here is an example that shows InSim.NET (Spark 2) running under IronPython. It's just a simple example that shows maintaining the connection list.

import clr
clr.AddReference('System')
clr.AddReference('InSimDotNet')

from System import *
from InSimDotNet import *
from InSimDotNet.Packets import *

connections = {}

def NewConnection(insim, ncn):
connections[ncn.UCID] = ncn
print 'Added connection: %s (%d)' % (ncn.UName, ncn.UCID)

def ConnectionLeft(insim, cnl):
del connections[cnl.UCID]
print 'Connection left: %d' % cnl.UCID

try:
# Create new InSim object.
with InSim() as insim:

# Bind packet-handlers.
insim.Bind(PacketType.ISP_NCN, NewConnection)
insim.Bind(PacketType.ISP_CNL, ConnectionLeft)

# Initialize InSim.
settings = InSimSettings()
settings.Host = '127.0.0.1'
settings.Port = 29999
settings.Admin = ''
insim.Initialize(settings)

# Request connections to be sent.
tiny = IS_TINY()
tiny.ReqI = 1
tiny.SubT = TinyType.TINY_NCN
insim.Send(tiny)

# Stop program from closing.
Console.WriteLine('Press any key to exit...')
Console.ReadKey(True)
except Exception as ex:
print 'InSim Error: %s' % ex

One thing I've noticed with 2.0 Spark is that I'm unable to create buttons with Text containing 200+ characters Is this a bug or some kind of a limit by you?
Is there any reason for the NCN.UCID to be an integer? If not, then.. this is a bug?
As far as I can tell, in .NET pretty much every publically accessible property is int (instead of sbyte / short / etc). I think Darktimes is just trying to get on par with such .NET regulations.

It's just like how every decimal value in .NET is a double and you won't see float.
Thanks.

Needed to know if I have to make my functions compatible with that change.
(And now I do, obviously.)
Sorry for double-posting.

I wanted to report a possible bug - I tried to connect to a server, which has a nice and strong password, but I couldn't. I re-checked my settings more than a few times, connected to other servers, connected other insim apps to the problematic server. Everything worked. Could the reason for this be the special symbols in the password? I'm going to do some testing locally and update this post if I find out if that's it, and if yes - which char exactly is causing it.

Oh yes, also - I did connect to another LFS server on the same machine(same IP), and it worked flawlessly (different password, obviously :razz.

[E] So far, I found out that "?", "*", "<", ">", ":", "" (double quote)", "\" and "/" are causing problems.
Passwords "lol!@#$%^&", "lol[]{},." worked without a problem.
Also, symbols ";", "' (single quote)" don't seem to cause any problems.
** Double quote and backslash were properly escaped during the tests (\" , \\).
-
(DarkTimes) DELETED by DarkTimes
Any progress with this library? Long time we haven't heard from you DarkTimes
Quote from PoVo :Any progress with this library? Long time we haven't heard from you DarkTimes

The last message from him was deleted on 12th February 2011 @ 15:08, by himself.
Hey, sorry for not posting for a while.

I uploaded a new version of the library with some changes I made a while ago but didn't get around to uploading. I think I may have fixed the bugs with the string encoding.
So, has anyone had a chance to try those changes I made? I think it should be working OK, but don't want to declare it so until povo and broken have signed off on it (as it was their bugs I was fixing).

FGED GREDG RDFGDR GSFDG