The online racing simulator
Searching in All forums
(921 results)
DarkTimes
S2 licensed
Quote from DarkTimes :OK, I think I've fixed the bug with the BTN text. What was happening is the BTN code wasn't correctly taking into account the NULL terminator when figuring out the length of the string. Because there was no NULL terminator LFS was discarding the string and not showing it, which is why some buttons appeared to not be sent.

Although Scawen has fixed this bug in the latest LFS patch, I'm not going to change it in InSim.NET for the moment, as that would break backwards compatibly with old LFS versions (which still have the bug). For the time being the maximum length of the BTN text field will remain 239 bytes.
DarkTimes
S2 licensed
If you can show me an example of the struct, then I will compile a special version of InSim.NET for you with support for those games. I would need to know exactly what data the packet is to contain however, as without that I can't do anything. If you provide the specification, it's no problem to create a new build.

I'm not sure I'll add this to the main trunk, I may create a separate fork for it. It depends what data the struct contains really. You could create a fork for it yourself, if you're so inclined.
Last edited by DarkTimes, .
DarkTimes
S2 licensed
The enum is InSimDotNet.Out.DashLightFlags. You should not need to cast the value, it should already be a DashLightFlags type.
DarkTimes
S2 licensed
Quote from Dygear :[EDIT] Damn it DarkTimes, stop deleting your posts. It's making me look like I'm talking to myself!

I only delete my posts because they suck and I have no idea what I'm talking about.
DarkTimes
S2 licensed
Quote from broken :I am thinking that it's leaking memory..

Well, as I said in my post, leaking memory here might be an issue. You do need to 'unhook' any events which are attached to the timer before it goes out of scope. Dangling event-handlers are the number one cause of .NET memory leaks. This will be even more of an issue if your program is designed to run for weeks at a time, like a LFS server app.

Really the best practice is to try and reuse your timer objects wherever possible, however if that is not possible then you should unhook the event-handler once you are done with it, and before that object goes out of scope.

timer.Elapsed -= timer_Elapsed

This is one of the most common memory leaks in the .NET, if not the most common, so you are correct to raise this as an issue.
Last edited by DarkTimes, .
DarkTimes
S2 licensed
I got the HD 4670 plumbed in today, working great. Was looking at the retail price for them and you can pick one up for about £40. Seems a pretty reasonable card for that price, although I'm obviously not an expert.
DarkTimes
S2 licensed
Thanks, Scawen!
DarkTimes
S2 licensed
Hey Scawen, you might like to look at this issue with the BTN packet.

Basically the BTN Text field does not need a trailing zero unless the TEXT_SIZE is exactly 240 characters. To clarify, if the TEXT_SIZE is a multiple of four but less than 240, say eight or 128, then no null terminator is needed. However if the TEXT_SIZE is exactly 240 characters and there is no trailing zero, then LFS fails to display the button and there is no error message or warning.

Just to note that the new MTC packet always needs a trailing zero, no matter what the TEXT_SIZE is (and warns you if you omit it), so it would seem there is a bug/inconsistency here.

I hope I've explained it clearly.
DarkTimes
S2 licensed
I've pushed out a release for InSim.NET 2.0.8.

Changes
  • Support for InSim 5, added IS_CON etc.. (now supports both InSim 4 and 5)
  • Fixed several bugs related to sending strings containing double-byte characters
  • Updated documentation help file with new packets
I've flagged it as a beta for now, in-case anyone finds more pressing bugs that need to be fixed.

As always you can get the latest release on CodePlex.
DarkTimes
S2 licensed
Yes, I've tested it.

If you send a button that's a multiple of four then it gets displayed, despite lacking a trailing zero. However if the text is exactly 240 characters then it does not get displayed, unless you replace the last char with a '\0'.

Here is a Python script I used to test the issue. I send three buttons, first eight chars long (no trailing zero), second 240 chars long (no trailing zero), third 239 + trailing zero. Only the first and third buttons get displayed. There is no error message in LFS about the button, it seems it's just discarded.

import socket
import struct

sd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sd.connect(('127.0.0.1', 29999))

# Init InSim.
isi = struct.pack('4B2HBcH15sx15sx',
44, # Size
1, # Type
0, # ReqI
0, # Zero
0, # UDPPort
0, # Flags
0, # Sp0
'\x00', # Prefix
0, # Interval
'', # Admin
'Test',) # IName
sd.send(isi)

# Send button with eight chars of text (displayed)
btn = struct.pack('12B8s',
20, # Size
45, # Type
1, # ReqI
255, # UCID
1, # ClickID
0, # Inst
0, # BStyle
0, # TypeIn
20, # L
20, # T
40, # W
10, # H
'12345678',) # Eight chars long
sd.send(btn)

# Create a str 240 chars long.
text = ''.join(['a' for i in xrange(0, 240)]) # Create str.

# Send button with exactly 240 chars (not displayed)
btn = struct.pack('12B240s',
252, # Size
45, # Type
1, # ReqI
255, # UCID
2, # ClickID
0, # Inst
0, # BStyle
0, # TypeIn
20, # L
40, # T
40, # W
10, # H
text,) # 240 chars long
sd.send(btn)

# Create str 240 chars with trailing zero
text = ''.join(['a' for i in xrange(0, 239)]) + '\x00'

# Send button 240 chars with trailing zero (displayed)
btn = struct.pack('12B240s',
252, # Size
45, # Type
1, # ReqI
255, # UCID
3, # ClickID
0, # Inst
0, # BStyle
0, # TypeIn
20, # L
60, # T
40, # W
10, # H
text,) # 240 chars with null terminator
sd.send(btn)

while True:
pass

Last edited by DarkTimes, .
DarkTimes
S2 licensed
Quote from MaKaKaZo :I used
(1+((text_len-1)/4))*4

because in the insim documentation it doesn't say anywhere that the "Text" field for the IS_BTN packet must end with a zero:
char Text[240]; // 0 to 240 characters of text

That's why I used that. I understand that in IS_BTN packets you don't need to add a terminating zero byte. In IS_MTC the situation is different as it states that the string must be zero terminated.

The BTN text sometimes does need a null terminator. If you send a BTN with text exactly 240 characters long (no trailing '\0') then LFS just discards the button with no error message. This was a bug I had to track down recently in InSim.NET.

Edit: It occurs to me now that this may actually be a bug in LFS.
Last edited by DarkTimes, .
DarkTimes
S2 licensed
The MSO packet works correctly, you need to use the TextStart property to get where the text starts. This is not InSim.NET, this is how the packet is sent by LFS.

var cmd = mso.Msg.SubString(mso.TextStart);

DarkTimes
S2 licensed
Quote from NeOn_sp :I have started to port my InSim app from LFSExternal, to this.

Great lib,

I have a trouble, and I don´t know why.

Reading MSO packets, if I write in server "!top" ( without slashes )

mso.Msg contains: "Player_Name ( without colours ) : !top" ( without slashes )..

Sorry, I don't understand what the problem is. Maybe you could try to go into more detail? As far as I can see that's working correctly.

If you want to process commands, you need to first set the Prefix char in InSimSettings.

insim.Initialize(new InSimSettings { Prefix = '!' });

Then to process the command:

void MessageOut(InSim insim, IS_MSO mso) {
if (mso.UserType == UserType.MSO_PREFIX) {
string command = mso.Msg.Substring(mso.TextStart);
if (command == "!top") {
// Do something...
}
}
}

Quote :And another question, If I want to send a MTC, it fails if lenght(msg)%4==0, and to solve this I have to add \0 at msg end. Wouldn´t be easier checking this internally, and add if necessary \0 in the own lib?

Sorry, that is a bug that was added in the latest revision, I've reverted the change.

Obviously if you pull the latest revision you should expect some bugs.
Last edited by DarkTimes, .
DarkTimes
S2 licensed
OK I think I've fixed the bug. It was another issue with the string length. You can get the latest revision from CodePlex.

The string encoding seems a little fragile, I'm thinking I may need to do something more to improve its robustness.

Edit: another push to CodePlex, fixed small bug introduced with this bug fix.
Last edited by DarkTimes, .
DarkTimes
S2 licensed
Ah, I musta missed that, sorry. Anyway I've recreated the string and it does indeed cause a crash, but I have no effing idea why. I'm looking into it.

^3» ^7Crash: ^7☆THC﹥^3Broken^7☆ ^7>< ^tGaz^t ^7(0.00 km/h)

I should save all these strings and make some unit tests or something.
DarkTimes
S2 licensed
What's the name of the other player in the crash? I need to know both of them.

Quote :If I need to hook up an event to InSim.InSimError, wouldn't that mean going into multithreading?

Um, no, the library is already multithreaded. If an exception gets thrown on a background thread you have no way of knowing. It will unwind that threads callstack and stops execution, but as each thread has its own callstack this won't affect your main program thread. Because of this I added the InSim.InSimError event that allows you to catch exceptions on background threads. You should pretty much always catch and log any errors raised by InSimError.
DarkTimes
S2 licensed
Quote from broken :Awhh! Sorry, forgot to save the replay.. :doh:
I've been distracted the whole day today, couldn't do a thing normally..

Globals.MS is simply the server message start character, to make the messages from the InSim app differ from normal text..:

<?php 
public static readonly string MS "^3» ";
?>

Sorry for being zero help, really, I don't know how I forgot to do that! Just.. stupid.. ;d
Anyway, I'll make sure to get a replay if/when it happens again.

What I need to know is the name of the two players. The error occurs in part of the code that converts a string into bytes, so I need to know what the string actually was to figure out what happened.

Quote :But IS_CON fails rarely. Also, it used to disconnect from the host, before I decided to catch the exceptions thrown from it. And just that - it disconnected, without crashing my app.

That's probably because the error is being thrown on the background thread, as I've said before you need to hook up an event to InSim.InSimError to catch those. Once an error is thrown InSim.NET can't really continue, can it, it doesn't know what state the program is in any more, so it disconnects.
Last edited by DarkTimes, .
DarkTimes
S2 licensed
OK send me the replay.

Edit: It works fine for me, so the bug must be somewhere else, so I need the replay.

private static void Collision(InSim insim, IS_CON con) {
IS_NPL a = _players[con.A.PLID];
IS_NPL b = _players[con.B.PLID];
double speed = MathHelper.MpsToKph((con.SpClose & 0x00ff) * 0.1);

insim.Send("^7Crash: " + a.PName + " ^7>< " + b.PName + " ^7(" + speed + " km/h)");
}

Also what does the Globals.MS string contain?
Last edited by DarkTimes, .
DarkTimes
S2 licensed
Change the 4 to a 5.

# Try to connect to LFS...
insim = socket.socket()
connected = False
while not(connected):
try:
print 'Attempting to connect to LFS on port', insimport, '...'
insim.connect(('localhost',insimport))
insim.send(is_isi.pack(44,1,1,0,outsimport,0,0,0,0,'','CamLevel'))
version = is_ver.unpack(insim.recv(20))
[b]if version[6] != 5:[/b]
print 'Wrong InSim version, (expected 4, got', version[6],',) exiting...'
time.sleep(5)
sys.exit()
else:
connected = True
print 'Connected to LFS...'
except socket.timeout:
print 'LFS did not respond to connection attempt, waiting 5 seconds...'
time.sleep(5)
except socket.error:
print 'Could not connect to LFS, check that InSim ports match.'
time.sleep(5)

DarkTimes
S2 licensed
OK - I've pushed some fixes onto CodePlex, you can pull the latest version from the repository.

The bug was caused because the size of the BTN packet was being determined before the string had been converted into bytes, which meant that any string where the number of bytes > number of characters would cause it to crash. Anyway, I've fixed it, I hope.

I also fixed the new MTC packet to make it variable sized, plus made a couple of tweaks to IS_CON.
DarkTimes
S2 licensed
I've figured out where the bug is, but it will take me some time to fix it, I need to think about it for a while.
DarkTimes
S2 licensed
The trick is to listen to this while going to work at 6am.

http://www.youtube.com/watch?v=AHAipEZTjFg
DarkTimes
S2 licensed
The timer gets disposed when the timer object is removed from scope, just like all .NET objects (more or less). The only thing you need to watch out for is that there are no stray event-handlers or references lurking in the background. These can cause memory leaks and other issues, but this is the same for all .NET objects, not just timers.

There are at least five different timers in .NET (from the top of my head) and each one works slightly differently. The only way to know how to use each one is to read the documentation (select the object and press F1 in VS2010).

Really the only important thing you need to know is that if there is a possibility that two threads may try to access a resource at the same time, then you will need to synchronise the access with locks. If you don't your app will work 99.9% of the time, but that last 0.1% will make you go insane.
Last edited by DarkTimes, .
DarkTimes
S2 licensed
Quote from broken :Oh man.. Multithreading - not my territory.

Well, love or hate multithreading (um, I think everyone hates it), InSim.NET is a multithreaded library, and each timer you add spins up yet another thread. A console application using InSim.NET with a timer will result in three threads running at once, the main program thread, the InSim.NET* receive thread, plus also the timer thread.

Now if you try to update a global variable from a timer while the receive thread is trying to update it, you will run into serious and strange bugs (like this one). The solution is that you need to employ locking, which is pretty-standard for multithreaded apps, and something .NET makes pretty simple, to its credit.

You need to create some sort of object that you can lock on.

object lockObject = new object()

Whenever you manipulate QueueHolder (or any other variable which can be access from multiple threads), you call lock on that object to prevent anyone else from trying to update it at the same time. For instance.

lock (lockObject)
{
QueueHolder.Add(object);
}

If another thread tires to access the QueueHolder it will first run into the lock, which will cause that thread to block until the lock is released. This makes it sure that only a single thread can access that resource at once, however it has the downside that every other thread will need to wait around for the first thread to finish. You need to do this at every point you update or access QueueHolder though.

The code you posted in that screenshot would become something like this (simplified and without testing)

public static void QueueProcessor_Elapsed(object source, ElapsedEventArgs e)
{
int[] UCIDTimes = new int[256];

lock (lockObject)
{
int Count = QueueHolder.Count;

for (int i = 0; i < Count; i++)
{
if (QueueHolder.Count > 0 && QueueHolder[0] != null && QueueHolder[0].Button != null)
{
int UCID = QueueHolder[0].Button.UCID;
int idx = Dizplay.getUserIdxByUCID(UCID);

if (UCIDTimes[UCID] < maxPerUCID)
{
clsUser U = Dizplay.Users[idx];

Dizplay.InSim.Send(QueueHolder[0].Button);
U.Buttons[QueueHolder[0].Button.ClickID] = CopyButton(QueueHolder[0].Button);
UCIDTimes[UCID] += 1;

QueueHolder.RemoveAt(0);
}
}
}

if (QueueHolder.Count < 1)
{
QueueProcessor.Stop();
QueueProcessor.Enabled = false;
}
}
}

In this instance we just wrap the whole thing in a lock, which should do for these purposes, although really you want to make your locks as small and 'finely-grained' as possible. As I say, that's simplified and without testing through, but it should give you a good idea how to progress.

For a really nice primer on threading in C# have a read of this free e-book (chapter two on synchronisation should be of special interest to InSim.NET users).

http://www.albahari.com/threading/

Threading is one of those things that seems so simple on the surface, until you run into your first concurrency bug and realise that it's a black pit of death you can never escape from. Oh well...

* This is the same for every InSim library ever released, it's not peculiar to InSim.NET. It shows how weird and strange these bugs can be as to how seldom this has come up. You can guarantee that they will come up eventually though, given enough time. Incidentally this is why Windows doesn't let you update controls from different threads (and why you need to marshall invokes across the dispatcher), by allowing cross-thread access they'd open themselves up to thousandths of strange bugs they'd never be able to figure out.

Edit: Another option would be to make InSim.NET appear to run single-threaded by using a SynchronizationContext, which is possible and I might write an example of how to do that later. It would make the library much less efficient though, which is why it doesn't do that by default.
Last edited by DarkTimes, .
FGED GREDG RDFGDR GSFDG