The online racing simulator
Does a message come up within LFS before it closes? It could be that you are not sending the ISI initialisation packet, alternatively it could be that the game admin password you've supplied is incorrect.

Another cause could be that you've neglected to include a call to InSim.run() in your program, which prevents your program from exiting while the connection is still active. If you look at the example code I posted you can see run() being used.

import pyinsim

def connectionJoined(insim, ncn):
print 'Username', ncn.UName, 'joined host'

insim = pyinsim.insimConnect('localhost', 29999, IName='^3pyinsim')
insim.bind(pyinsim.ISP_NCN, connectionJoined)
[b]insim.run()[/b]

A Python program will exit as soon as it reaches the end of the main program thread, regardless of any background threads which may be running (like the ones in pyinsim listening for packets). When this happens Python will release all the resources used by your program, causing the socket connection with LFS to be closed. Calling run() prevents this by telling your program to wait until all the background threads have completed before continuing.
Also make sure you're binding EVT_ERROR.


def insim_error(insim, error):
print 'InSim error: ', error

insim.bind(pyinsim.EVT_ERROR, insim_error)

If you are running insim.run() as DarkTimes mentioned above, it could very well be that your code is bombing out somewhere and failing silently because the error happens on a child thread (it won't get caught on the main thread where you're doing all your work). This has happened to me.
Quote from DarkTimes :Does a message come up within LFS before it closes? It could be that you are not sending the ISI initialisation packet, alternatively it could be that the game admin password you've supplied is incorrect.

Another cause could be that you've neglected to include a call to InSim.run() in your program, which prevents your program from exiting while the connection is still active. If you look at the example code I posted you can see run() being used.

cut

A Python program will exit as soon as it reaches the end of the main program thread, regardless of any background threads which may be running (like the ones in pyinsim listening for packets). When this happens Python will release all the resources used by your program, causing the socket connection with LFS to be closed. Calling run() prevents this by telling your program to wait until all the background threads have completed before continuing.

I've got the initialization packet, the run(), and the correct admin password

The end of my code:
try:
insim.connect('192.168.1.104', 29999)
insim.send(pyinsim.ISP_ISI, Admin='Not For You to Know', IName='^3pyinsim')
insim.send(pyinsim.ISP_TINY, ReqI=1, SubT=pyinsim.TINY_NPL)
insim.run()
except pyinsim.socket.error, err:
print 'InSim Error:', err[1]

And right before that:
insim = pyinsim.InSim()
insim.bind(pyinsim.ISP_NCN, connectionJoined)

Quote from tmehlinger :Also make sure you're binding EVT_ERROR.


def insim_error(insim, error):
print 'InSim error: ', error

insim.bind(pyinsim.EVT_ERROR, insim_error)

If you are running insim.run() as DarkTimes mentioned above, it could very well be that your code is bombing out somewhere and failing silently because the error happens on a child thread (it won't get caught on the main thread where you're doing all your work). This has happened to me.

Alright. I'll try it and see if it helps.

I also had a few return true/return false from the original code from my friend, and I removed those as morpha recommended. Code still works fine. But I still get the
InSim Guest Closed: pyinsim

in live for speed dedi server as soon as I connect. I literally watched the server via teamviewer as I connected, and as soon as I connect right after the Learjet45 connected (learjet45) line, the insim closes the connection to pyinsim or whatever.
I'll try that EVT_ERROR bind and see if that helps.

Edit: That did the trick! Thanks tme! Here is what I get:
InSim error: connectionJoined() takes exactly 4 arguments (2 given)

From python IDLE GUI.
If someone wants the code to look at, again feel free to PM me. I don't want it floating around for anyone to get hold of. My friend and I have tried to keeps our codes as private as possible, especially when it is developed specifically for our servers (in which case, the original code he gave me was made specifically for our clan's server).
Quote from learjet45 :I also had a few return true/return false from the original code from my friend, and I removed those as morpha recommended.

Just so you understand, they were not syntactically incorrect, just pointless

Edit: Like I told you via PM, connectionJoined() cannot take more than 2 parameters
Edit again: To avoid confusion, that's ignoring the instance reference (self).
Well alrighty then.
New idea time. I'll see how this new idea goes and see what I can do.
Warning, I'm planting the seed for a free software argument...

Since it appears that you're new to Python/InSim programming, you might as well just post your code. I doubt you or your friend are doing anything really revolutionary with InSim that warrants keeping the code secret. Furthermore, if/when you're making mistakes, it's extremely likely that we've made the same mistakes ourselves and we'll be able to advise you better.

By the same token, it's a nice gesture to share your code since you're using someone else's freely given code in the same application.
Quote from tmehlinger :Warning, I'm planting the seed for a free software argument...

Since it appears that you're new to Python/InSim programming, you might as well just post your code. I doubt you or your friend are doing anything really revolutionary with InSim that warrants keeping the code secret. Furthermore, if/when you're making mistakes, it's extremely likely that we've made the same mistakes ourselves and we'll be able to advise you better.

By the same token, it's a nice gesture to share your code since you're using someone else's freely given code in the same application.

I'm not totally new to Python, and neither is my friend.
he just doesn't like his codes floating around in the open without his consent.
I have no problem sending you the code. If you PM me I'll send you a link. I'm just not used to Insim programming. I have some basic experience with Python. I'm no major coder or anything, but it's not like I have never written a single line of code in my life.
Trying to make this work again http://www.lfsforum.net/showthread.php?t=49607. It seemed to work on the earlier versions of pyinsim.

When using python 2.6 and pyinsim 1.5.5 I get multiple errors.

Looks like I was able to fix some minor syntax errors but I'm stuck on the .Packet arguments(line 222). Would be nice if you guys could have a look a it. I'm an absolute beginner at this...
Yeah, that's from a really, really old version of pyinsim. I will have a go at porting it to pyinsim 1.5 tomorrow. Shouldn't take too long.
Not that many people care probably, but I'm going to release a new version of this soon, maybe tomorrow. The new features are:
  • Vastly improved performance (pre-compilation, slots, general optimisations...)
  • Improved error reporting (no hooking up separate error events)
  • Simplified API (easier ways to do stuff)
  • Seamless support for UDP (no dealing with seperate API calls, timeouts etc..)
  • Even more helper functions (sending messages, commands, packets etc..)
  • A free cookie...
And coming soon we'll have...
  • .NET 4.0 support (DLR)
I've been working on this for a little while now, but it's really started to come together in the last few days. I'm starting to feel very pleased with it, especially how it supports UDP, which has caused some big changes to the API. Much of the code has been rewritten, and is now much improved. Turns out it's easier to write code when you're sober... who knew?
Looking good, especially the cookie part
I've been busy myself and, among other things, discovered that Shift-JIS is not the correct codec for Japanese, proven by the fact that it fails to de- and encode high mappings (CJK characters). Instead, cp932, which is Microsoft's Shift-JIS extension, should be used.

I've written a separate module called strmanip for string manipulation, it includes a slightly modified strToUnicode (renamed to toUnicode, since it's part of a string related module anyway), it's counterpart fromUnicode, stripColours and escapeString. I want to add more, I just don't know what at this point. Open for ideas, perhaps some applications require some common string manipulation that I just can't see. Perhaps a function to build car strings from lists?

So far it works fine, there's just one problem with fromUnicode; CJK characters are unified in Unicode, making it impossible to correctly identify a language if only CJK characters are present. For performance reasons, fromUnicode only performs a single character look-behind. The pleasant side-effect of this is that it usually generates short strings, using the same encoding for as much of the string as possible. For example, all character sets include the 128 ASCII characters, so it's not necessary to switch encoding for any of those.

What I also want to do is a language module, like LFSLapper and Airio provide.
I'll post some of my ideas regarding that, as well as strmanip, later when I've got something to show

On a side note: Anyone tested python 2.7 yet? I'm looking forward to it I must say
Yeah, I'm really up for the cookie thing as well.
(See, we do care)
Quote from DarkTimes :
  • Seamless support for UDP (no dealing with seperate API calls, timeouts etc..)

Any chance you can make OutGauge over InSim work?

I recently made my own modifications to pyinsim to support the InSim relay. I still need to work out a few bugs, but if you're interested, I'll send you my changes.
OutGauge over InSim works at the moment (correct me if I'm wrong), but currently you need to create a separate OutGauge object to receive the packets. But yes, the new version will handle packets received on a separate UDP connection without any extra work. In fact you don't need to do anything except bind events for the packets you want to receive. There is still a separate class for standalone OutGauge/OutSim programs, which is a design consideration as opposed to a technical one.

To be honest I've never used InSimRelay, I'd need to look into how to support it. You can send me your code if you want and I'll consider what I'll need to do to add it.
Quote from morpha :Instead, cp932, which is Microsoft's Shift-JIS extension, should be used.

OK - noted, thanks.

I would be very interested in a separate string manipulation module. At the moment the strToUnicode stuff was added simply so I could display LFS strings in wxPython apps with something resembling the correct encoding. In an ideal world pyinsim would convert strings to and from unicode, and in Python 3.0 as all strings are unicode, it could do this by default for each string without any input from the user. I've really not spent the time I'd need to on this functionality though, so it would make my life 100 times easier if it already existed.
Yet another "it's entirely insignificant but who gives a damn " mini-tweak, pyinsim.time(ms):
  • Drop the floor, casting to int will always floor anyway.
  • minutes can be converted with one division less:
    m = int((ms / 60000) % 60)

Attached you will find a stripped down version of strmanip as it is now, it's quite large because it contains character encoding look-up tables. Some of those come with python in the encodings module, but unfortunately not all, so I decided it's best to just include them. Unfortunately it's mostly undocumented, if you need any specific info, just ask and I'll try to explain

Regarding your ideal world: Whether to convert to and from Unicode automatically really depends on the application's purpose. If, for example, the strings remain "in LFS", i.e. are only stored by the by the application to be displayed later, possibly unmodified, converting them would be a waste of resources. With that in mind, I would advise you against having pyinsim convert everything automatically, because the application may not need to display a string outside of LFS at all.

On another note: As I've mentioned before, CJK characters are not guaranteed to be identified correctly because they are shared among several languages. This means that you should never convert values you wish to compare to values from LFS without converting those as well. Ideally, you wouldn't convert them at all as converting them to Unicode could potentially produce collisions, if for example two player names contain the same character at the same position, but from different encodings.

Yet another note: In my ideal LFS world, we'd simply have UTF-8 or UTF-16
Attached files
strmanip.rar - 99.5 KB - 300 views
Quote from morpha :
m = int((ms / 60000) % 60)

[/list]

So it was you that thought me that! Got any tips for returning a string with hours, minutes and seconds?

h = <your code here>;
m = int((ms / 60000) % 60);
s = <your code here>;

printf("You've been playing for %d hours, %d minutes and %d seconds", h, m, s);

I came up with this, but it would be used to convert seconds.

h = int((time / 3600) % 60);
m = int((((time - (h * 3600)) / 60) % 60);
s = int((time - ((h * 3600) + (m * 60))));

You could use the same function and simply multiply your input integer in seconds by 1000, making it milliseconds, or you could simply shift the formulas "one up": (assuming PHP)
$h = $time / 3600;
$m = $time / 60 % 60;
$s = $time % 60;

Casting is only required if the results are not used in a formatted string.
€#0: Actually the same applies to python, meaning explicit casting is unnecessary in timeStr, format does this automatically if a float is supplied for a %d.

€#1: Here's what I'd consider the perfect timeStr() and time():
def time(ms):
"""Convert milliseconds into hours, minutes, seconds and thousanths.

@type ms: number
@param ms: The time in milliseconds

@rtype: tuple
@return: A tuple containing the (hours, mins, secs, thousandths).

"""
h = ms / 3600000
m = ms / 60000 % 60
s = ms / 1000 % 60
t = ms % 1000
return (h, m, s, t)

def timeStr(ms, hour_fmt = False):
"""Convert milliseconds into a formatted time string (EG h:m:s.t).

@type ms: number
@param ms: The time in milliseconds.
@type hour_fmt: boolean/string
@param hours: Either boolean True to enforce inclusion of hours, even if 0, or a custom format string. Defaults to False for neither.


@rtype: string
@return: The formatted time string (E.G. hh:mm:ss.ttt)

"""
h, m, s, t = time(ms)
if isinstance(hour_fmt, str):
return hour_fmt % {'h':h, 'm':m, 's':s, 't':t}
if hour_fmt:
return '%d:%02d:%02d.%03d' % (h, m, s, t)
else:
return '%d:%02d.%03d' % (m, s, t)

€#2: This allows for very flexible formatting, like:
>>> timeStr(float(5234567), 'You have been playing for %(h)0.1f hours.')
'You have been playing for 1.5 hours.'

Casting to float because the result is always of the same type the input is.

or Dygear's example:
>>> timeStr(5234567, 'You\'ve been playing for %(h)d hours, %(m)d minutes and %(s)d seconds')
"You've been playing for 1 hours, 27 minutes and 14 seconds"

All right, here we go. I've attached my full, modified pyinsim module based on 1.5.5. I have also attached just the diff. Note the .txt extension because the board won't permit .diff or .py.

Here's some sample code that:
  • connects to the relay
  • grabs the host list
  • selects a random host with active connections that doesn't require a spectator pass
  • dumps the connected UCIDs

import pyinsim_relay as pyinsim
import random
import time

class RelayTest(object):
def __init__(self):
self.hosts = []

insim = pyinsim.InSim(use_relay=True)
insim.connect()

insim.bind(pyinsim.IRP_HOS, self.handle_hostlist)
insim.bind(pyinsim.IRP_ERR, self.handle_relay_error)
insim.bind(pyinsim.ISP_NCN, self.handle_new_conn)
insim.bind(pyinsim.EVT_ERROR, self.handle_insim_error)

insim.send(pyinsim.IRP_HLR)
time.sleep(5)

host = pyinsim.stripColours(random.choice(self.hosts))
print 'Selecting host: ', host
insim.send(pyinsim.IRP_SEL, HName=host)
time.sleep(2)

insim.send(pyinsim.ISP_TINY, SubT=pyinsim.TINY_NCN, ReqI=1)

try:
insim.run()
except pyinsim.socket.error, err:
print 'InSim error:', err[1]

def handle_hostlist(self, insim, hos):
for host in hos.Hosts:
if host.NumConns > 1 and not host.Flags & 1:
self.hosts.append(host.HName)

def handle_relay_error(self, insim, err):
print 'InSim Relay error:', err.ErrNo

def handle_new_conn(self, insim, ncn):
print 'New connection, UCID:', ncn.UCID

def handle_insim_error(self, insim, err):
print 'InSim error:', err

if __name__ == '__main__':
RelayTest()

Note that once you've successfully selected a host, you receive packets as though you'd connected directly to the host instead of using the relay. No extra work involved.
Attached files
pyinsim_relay.py.txt - 125.6 KB - 415 views
pyinsim_relay.diff.txt - 7.9 KB - 353 views
Quote from morpha :Yet another "it's entirely insignificant but who gives a damn " mini-tweak, pyinsim.time(ms):
  • Drop the floor, casting to int will always floor anyway.
  • minutes can be converted with one division less:
    m = int((ms / 60000) % 60)

Attached you will find a stripped down version of strmanip as it is now, it's quite large because it contains character encoding look-up tables. Some of those come with python in the encodings module, but unfortunately not all, so I decided it's best to just include them. Unfortunately it's mostly undocumented, if you need any specific info, just ask and I'll try to explain

Regarding your ideal world: Whether to convert to and from Unicode automatically really depends on the application's purpose. If, for example, the strings remain "in LFS", i.e. are only stored by the by the application to be displayed later, possibly unmodified, converting them would be a waste of resources. With that in mind, I would advise you against having pyinsim convert everything automatically, because the application may not need to display a string outside of LFS at all.

On another note: As I've mentioned before, CJK characters are not guaranteed to be identified correctly because they are shared among several languages. This means that you should never convert values you wish to compare to values from LFS without converting those as well. Ideally, you wouldn't convert them at all as converting them to Unicode could potentially produce collisions, if for example two player names contain the same character at the same position, but from different encodings.

Yet another note: In my ideal LFS world, we'd simply have UTF-8 or UTF-16

Yes, I would love for LFS to support in UTF-8, but sadly that's unlikely to happen. I see what you mean about decoding strings that don't get used, probably in the long run I will leave the unicode encoding/decoding stuff as separate functions. I can think of several ways where I could have the string only decoded when it's actually accessed, but this would probably add an unnecessary layer of complexity, which is something I always try to avoid when designing libraries. In my experience it's a mistake to try to hide something from the user, as eventually the fact you're hiding it will just confuse things (the law of leaky abstractions).

Thanks for providing this code though, it's interesting. I plan to add a strFromUnicode function to the next release, if that's OK with you. Obviously I will give you credit for having written it.
Quote from tmehlinger :All right, here we go. I've attached my full, modified pyinsim module based on 1.5.5.

Awesome thanks, I'll pick it apart and see what's what.
Quote from DarkTimes :Thanks for providing this code though, it's interesting. I plan to add a strFromUnicode function to the next release, if that's OK with you. Obviously I will give you credit for having written it.

Sure

I'm also looking into Cython and SIP, both of which should allow for significant performance improvements if used correctly. Though for now, functionality is more important to me than performance, so don't expect any results anytime soon
Quote from tmehlinger :In addition to this, if you want to squeeze out even more performance (well... mostly just lower memory footprint), consider using __slots__. Note that you'd need to update your packet classes to new-style class definitions, but that would be an easy search-and-replace.

http://docs.python.org/reference/datamodel.html#slots

I think I'm done spamming the thread now.

In retrospect converting the packets to use __slots__ isn't a great idea. The actual memory footprint you save is minimal and unless you are creating millions of objects doesn't really impact on performance. Add to this that it limits what you can do with an object, that you cannot extend the packets dynamically, I have decided not to use them. I may leave __slots__ on MCI, NLP, OutGauge and OutSim packets, as you can have many of those instantiated every second, so it might have some use there.
PYINSIM 1.6.4 BETA

I've uploaded pyinsim 1.6 BETA below. The new version of the library has been largely rewritten and contains many changes and additions, especially to the core InSim class. The idea behind this release is to clean up a lot of the old guff left over from earlier versions, so I can focus on pyinsim 2.0 and Python 3.0 support. That being said it is still the best version of pyinsim evar!

I'll try to give a rough overview of what's new in pyinsim 1.6!

Documentation

The documentation for the module has been rewritten and brought up date, with lots more information and better organisation, which should make using the documentation much easier. As before you can access the full API reference by opening the file 'documentation.html' included in the package download.

Examples

The examples included in the package have been rewritten and brought up to date as well, and several more new ones have been added! Again you can access the examples by browsing the 'examples' folder once you have unzipped the package.

Helper Functions

All of the old helper functions have been tweaked, tested and brought up-to-date, and in addition several (hopefully) useful ones have been added.
  • strFromUnicode() - Convert a unicode string back into a LFS encoded string (thanks to morpha )
  • collision() - Determine if two rectangles are colliding
  • timeToMs() - Convert time list [h, m, s, t] back into milliseconds
  • newEvt() - Create an event identifier for creating and raising your own custom events
  • version() - Determine if the correct version of pyinsim is installed
Exceptions

A lot of work has been done on exceptions and pyinsim will no-longer raise general socket.errors, but instead has a set of new unique exception classes.
  • InSimError - General InSim related errors
  • InSimVersion - Incorrect version of InSim detected (more below)
  • OutError - General OutSim and OutGauge related errors
  • OutTimeout - An OutSim or OutGauge connection has timed out
In addition you no longer need to bind an event to EVT_ERROR to catch exceptions on the internal packet receive thread (although you can if you still want). Errors which occur on internal threads are now printed to std::err like all exceptions, which makes debugging pyinsim programs much easier and less confusing!

Performance

The overall performance of the module has been improved, through the use of pre-compiled structs and other code changes. As discussed in posts above __slots__ are used for MCI, NLP, OutGauge and OutSim packets, in order to reduce the memory overhead of creating these packets when using a small receive interval. For other packets the __slots__ are available but have actually been commented out, so you can reactivate them by doing a find and replace on 'pyinsim.py' and changing '#__slots__' to '__slots__'. It's high-tech engineering, I know.

API

As mentioned above most of the API changes reside in the InSim class, which has been largely rewritten. The most important change is that the InSim.connect() method has been removed and replaced with the new InSim.init().

The new Insim.init() method connects to LFS, sends the ISI packet, checks the InSim version, and starts a separate UDP connection when necessary. This means that the InSim connection process has been simplified to a single function call.

import pyinsim

insim = pyinsim.InSim()

try:
insim.init('localhost', 29999, IName='^3pyinsim') # Optional ISI args

except pyinsim.InSimVersion:
print 'Invalid InSim version detected'
except pyinsim.InSimError as err:
print 'InSim Error:', err

This code-snippet also demonstrates how to use the new exception classes, both to catch a InSimVersion exception, and a general InSimError.

The second major change is that InSim.run() has been removed, as the new socket code makes it obsolete. In pyinsim 1.6 your program should remain active as long as LFS is connected (hopefully )! Some more testing may be needed on this functionality, but it certainly works with Python 2.6.4.

In additon several old methods have been changed into instance variables, mainly InSim.isConnected(), InSim.getName() and InSim.setName(). This simply means that whereas before you did:

if insim.isConnected():
pass

You would now do:

if insim.connected:
pass

Lastly several new methods have been added that add some extra functionality to the class:
  • InSim.sendm() - Shorthand for sending MST and MSX packets
  • InSim.timer() - Execute a function callback after a timeout has elapsed
  • InSim.event() - Raise a custom event
It is recommended that you checkout the full API reference to see all of the additions and changes, as it's hard to sum everything up in a few paragraphs. Obviously it goes without saying that these changes make pyinsim 1.6 incompatible with code written for previous versions, but hopefully these adjustments mean that I will not have to change the API for future release and can work on keeping future versions backwards compatible. That being said, the beta API could still change subtly before 1.6 goes final.

Download

So there we have what's new in pyinsim 1.6 BETA. You do need to have Python 2.6 in order for it to run, which you can download from the official Python site. For installation instructions checkout the readme.txt file included in the download.

Note: As it is a beta it may still have one or two bugs, and some things may change before the final release, so you have been warned. Aside from that I'd be happy hear any comments or criticism you have regarding the module.
Attached files
pyinsim_1.6.4_beta.zip - 426.9 KB - 368 views
Very cool, going to play with it tonight.

Any chance you pulled the InSim relay stuff in, or will I have to modify it?

FGED GREDG RDFGDR GSFDG