The online racing simulator
First InSim move
1
(48 posts, started )
First InSim move
Hello. This year I finish high school and I got the title of technician for computer. I studied programming for 4 years (Pascal, C, C++ and C#) and I want to make my first Insim aplication that should make server statistics. (For each player: Top speed at diferent track, total laps, laps on each track... One file for each player). I read a bit about it on the forum and I was able to connect to server but still unable to manage with statistics. Help me to make first insim moves. Thank you very much

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using InSimDotNet;
using InSimDotNet.Packets;

namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{

// Create new InSim object
InSim insim = new InSim();

// Initailize InSim
insim.Initialize(new InSimSettings {
Host = "127.0.0.1", // Host where LFS is runing
Port = 29999, // Port to connect to LFS through
Admin = "xxx", // Optional game admin password
});

// Send message to LFS
insim.Send("/msg Hello, InSim!");

}
}
}

You should read the InSim.txt file shipped with LFS. It's basically a C header file and it describes in detail all the packets used by InSim. This file itself shoud provide you with enough info.

You'll have a bit of a hard time getting telemetry readings for InSim. InSim is more oriented at automated server administration and race control. To get some real telemetry data you'd have to use OutGauge/OutSim, but that has its own limitations too. InSim provides only basic info like speed, position and heading in CompCar structure in IS_MCI packet.
I continued my work... and now have problem... want to make commands like $exit... I talked with EQWorry and he said that i need to define $ as special char. How to do that? Then $exit would be a special text and LFS will report it as such
You've got to specify prefix to use commands starting with specific characters. Something like this:


//Initailize InSim
insim.Initialize(new InSimSettings {
Host = "127.0.0.1",
Port = 29999,
Prefix = '$',
});

Ok... I do that...

insim.Initialize(new InSimSettings
{
Host = "127.0.0.1",
Port = 29999,
Admin = "",
Prefix='$',
});

static void MessageOut(InSim insim, IS_MSO mso)
{
// Print out the message content.
Console.WriteLine(mso.Msg);
string a;
a=mso.Msg;

if (a=="$exit")
{
insim.Send("/msg OK");
}
}

And this not working... I guees that problem is in second code.
I'm not entirely sure about this at the moment, but I guess that LFS truncates the prefix so the message your app receives is just "exit".
Withaout $ same think... Maybe I need to remove first part of message, (for example SERT•Fangio: $exit)
Or you could do


if(a.Contains("$exit"))
insim.Send("/msg OK");

It doesn't truncate the prefix, it's probably the player's name, as was stated. I normally do something like this:


<?php 
if (mso.UserType == UserType.MSO_PREFIX) {
    var 
commands mso.Msg.Substring(mso.TextStart).Split();

    if (
commands.Any()) {
        var 
command commands.First().ToLower();

        switch (
command) {
            case 
"$exit":
                
// Handle command
                
break;
        }
    }
}
?>

Both ways works ok... Another question... How to send message just to one player... I can not figure out
You use the IS_MTC (message to connection) packet:

insim.Send(new IS_MTC { UCID = ucid, PLID = plid, Msg = "Hello, InSim!" });

But InSim.NET contains a couple of little convenience method for doing it.

To send a message to a specific connection:

insim.Send(ucid, "Hello, InSim!");

Or to send to a specific player:

insim.Send(0, plid, "Hello, InSim!");

This has helped a lot... What about buttons. I guess starting like this
insim.Send(new IS_BTN....

static void newconnection(InSim insim, IS_NCN ncn)
{
insim.Send(ncn.UCID,"^7Dobro dosao na server");
IS_BTN btn = new IS_BTN();
btn.H = 100;
btn.L = 100;
btn.T = 100;
btn.Text = "OKKK";
btn.UCID = ncn.UCID;
btn.W = 100;
btn.ClickID = 5;
btn.ReqI = 245;
btn.BStyle = ButtonStyles.ISB_CLICK;
insim.Send(btn);
}
static void deletebutton(InSim insim, IS_BTC btc)
{
IS_BFN bfn=new IS_BFN();
bfn.ClickID = btc.ClickID;
bfn.UCID=btc.UCID;
bfn.ReqI = btc.ReqI;

insim.Send(bfn);
}

I want to delete button, where i am wrong
You're not telling LFS what do to in the IS_BFN packet. You have to specify the SubT to tell LFS that you want to delete a button. Also the ReqI is supposed to be 0, but I guess LFS wouldn't mind about that.
Is there shorter way to sending buttons?
No, buttons are bit too varied to simplify into a couple of method calls. Maybe I'll think about adding something to help with them in the future.

The only thing different I would do is to send them like this, as I think this looks a lot neater. But it's just a preference thing.

insim.Send(new IS_BTN {
H = 100,
L = 100,
T = 100,
W = 100,
Text = "OKKK",
UCID = ncn.UCID,
ClickID = 5,
ReqI = 245,
BStyle = ButtonStyles.ISB_CLICK
});

Ok...

static void MessageOut(InSim insim, IS_MSO mso)
{
// Print out the message content.
Console.WriteLine(mso.Msg);

if (mso.UserType == UserType.MSO_PREFIX)
{
var commands = mso.Msg.Substring(mso.TextStart).Split();

if (commands.Any())
{
var command = commands.First().ToLower();

switch (command)
{
case "!exit":

insim.Send("/msg ^3Program se gasi!");
System.Threading.Thread.Sleep(1000);
insim.Disconnect();
Environment.Exit(0);
break;
}
}
}
}

I use this function for commands. I want to limit who can use this command. Think that I need to use PLID, but how PLID to connect with username? And how to send two or more flags in InSim setting vecause I want car contact reports and i need to set ISF_CON flag in the IS_ISI to receive car contact reports
insim.Initialize(new InSimSettings
{
Host = "127.0.0.1",
Port = 29999,
Admin = "",
Prefix='!',
IName="Fangio",
Flags = InSimFlags.ISF_MCI,

Interval = 1000, // The interval at which updates are sent (milliseconds)


});

Now you are getting into more complicated things. You have to add "directories" and add each player at NCN(NewConnection), CNL(ConnectionLeave) packets.


class Connections//Somewhere at the top of your code
{
public byte UCID;
public string UName;
public string PName;
public bool IsAdmin;
}

//Before "insim.Initialize(new InSimSettings blablabla"
insim.Bind<IS_NCN>(OnConnectionJoin);
insim.Bind<IS_CNL>(OnConnectionLeave);

//Add these anywhere you want
private void OnConnectionJoin(InSim insim, IS_NCN NCN)
{
try
{
_connections.Add(NCN.UCID, new Connections
{
UCID = NCN.UCID,
UName = NCN.UName,
PName = NCN.PName,
IsAdmin = NCN.Admin,
});
}
catch (Exception EX) { LogTextToFile("packetError", "NCN - " + EX.Message); }
}

private void OnConnectionLeave(InSim insim, IS_CNL CNL)
{
try { _connections.Remove(CNL.UCID); }
catch (Exception EX) { LogTextToFile("packetError", "CNL - " + EX.Message); }
}

LogTextToFile is a function i made to log text "type"(packetError in this case) to a file. PM me if you want it. You can just replace it for now to add the error to a richtextbox(just an example)

EDIT:
I forgot about flags. You can use more than one by using |
Example: Flags = InSimFlags.ISF_MCI | InSimFlags.ISF_MSO_COLS,

EDIT2:
Here is an example at MSO on how to use first code in case you don't know

case "!ialwaysforget":

insim.Send("/msg ^3" + _connections[mso.UCID].PName + " always forget his username which is " + _connections[mso.UCID].UName);
break;

I get this error "The name '_connections' does not exist in the current context"
How to connect PLID and PlayerName? I have PLID in car contact, but i want player name. And yes, send me code for logging. Thank you very much

Edit: Also need code for reading config files if you have?
I forgot to tell that after creating the class you will need to create the dictionary

private Dictionary<byte, Connections> _connections = new Dictionary<byte, Connections>();

About PLID you will have to do the same as UCID but with different packets, NPL and PLL.

LogTextToFile

const string SaveDataFolder = "files";

private void LogTextToFile(string file, string text, bool AdminMessage = true)
{
//Console.WriteLine(file + ": {0}", text); You dont need this one as you use form application.

if (AdminMessage == true) MessageToAdmins("There was a " + file + " : " + text);

if (System.IO.File.Exists(SaveDataFolder + "/" + file + ".log") == false) { FileStream CurrentFile = System.IO.File.Create(SaveDataFolder + "/" + file + ".log"); CurrentFile.Close(); }

StreamReader TextTempData = new StreamReader(SaveDataFolder + "/"+ file + ".log");
string TempText = TextTempData.ReadToEnd();
TextTempData.Close();

StreamWriter TextData = new StreamWriter(SaveDataFolder + "/" + file + ".log");
TextData.WriteLine(TempText + DateTime.Now + ": " + text);
TextData.Flush();
TextData.Close();
}

And MessageToAdmins which is used by LogTextToFile

private void MessageToAdmins(string Message)
{
foreach (var CurrentConnection in _connections.Values)
{
if (CurrentConnection.IsAdmin == true) MessageToUCID(CurrentConnection.UCID, Message);
}
}

You have to declare the _connections (or whatever you name it) variable first. InSim doesn't provide any PLID - UCID - Name matching, you've got to do this yourself. InSim.NET probably has some classes and functions to make the job easier...
now I get this error "An object reference is required for the non-static field, method, or property 'ConsoleApplication1.Program._connections'
"

namespace ConsoleApplication1
{

class Connections//Somewhere at the top of your code
{
public byte UCID;
public string UName;
public string PName;
public bool IsAdmin;

}
class Program
{
private Dictionary<byte, Connections> _connections = new Dictionary<byte, Connections>();
static void Main()
{

static void OnConnectionLeave(InSim insim, IS_CNL cnl)
{
try { _connections.Remove(cnl.UCID); }

catch (Exception EX) { };
}

static void Novakonekcija(InSim insim, IS_NCN ncn)
{
try
{

_connections.Add(ncn.UCID, new Connections
{
UCID = ncn.UCID,
UName = ncn.UName,
PName = ncn.PName,
IsAdmin = ncn.Admin,
});
}
catch (Exception EX) { }

You're accessing _connections from a static function which requires _connections to be declared as static too (if C# is anything like C++ or Java).
Ok. I fix that but I have trouble with PLID and Pname
This is for car contact
static void Kontakt(InSim insim, IS_CON con)
{

insim.Send("/msg " + _connections[con.A.PLID].PName);
insim.Send("/msg "+_connections[con.B.PLID].PName);
}

This is race joining
static void PrikljucenjeTrci(InSim insim, IS_NPL npl)
{
insim.Send("/msg "+npl.PName+" ^8je izasao sa "+npl.CName);
try
{
_connections.Add(npl.UCID, new Connections
{
PName=npl.PName,
PLID = npl.PLID,
});
}
catch { }
}

When there is car contact insim disconects from server. Can I use one class sonnections for everything or I need to separate?
Here is my npl and pll and cpr a bit modified for you. You will have to bind and create dictionary/class for this to work. But this is how packets are.

private void OnPlayerJoin(InSim insim, IS_NPL NPL)
{
try
{
if (_players.ContainsKey(NPL.PLID))
{
// Leaving pits, just update NPL object.
_players[NPL.PLID].PLID = NPL.PLID;
_players[NPL.PLID].PName = NPL.PName;
}
else
{
// Add new player.
_players.Add(NPL.PLID, new Players
{
PLID = NPL.PLID,
PName = NPL.PName
});
}
}
catch (Exception EX) {}
}

private void OnPlayerSpectate(InSim insim, IS_PLL PLL)
{
try { _players.Remove(PLL.PLID); }
catch (Exception EX) {}
}

private void OnPlayerRename(InSim insim, IS_CPR CPR)
{
try
{
_connections[CPR.UCID].PName = CPR.PName;
foreach (var CurrentPlayer in _players.Values) if (CurrentPlayer.UCID == CPR.UCID) CurrentPlayer.PName = CPR.PName;
}
catch (Exception EX) {}
}

Im sure "takeover" packet is needed too. But haven't managed(tested yet) on how to do it. Someone could help here for this too.

Anyway, usage of the code

_players[con.A.PLID].PName//This is the player name.

-
(M.M.L.) DELETED by M.M.L.
1

First InSim move
(48 posts, started )
FGED GREDG RDFGDR GSFDG