The online racing simulator
Hi D.T,

First, thanks for writing this, and making it available !

Except for the #@@#%#@^^ stupid indenting, I much prefer the interactive nature of Python rather than the overhead of a compiler.

I downloaded ver 1.55 that was in the first post of this thread, before I found this post (you really could start another thread ).

I had 1.55 working on my XP box, and was playing with the example scripts.

I was trying to output the speed and car.Node for a player, and it was sorta working.

I modified the example.4 script (kick player off if speed > 80 kph).

At Blackwood, the first node (starting gate was 299 .. it then went up to 310(?) .. and started over from 0 a few hundred feet from the first hairpin.

When I restarted the race, sometimes the script was working ... other times the node was increasing without the car even moving !

(Is that a bug that was fixed with 1.6.4 ?)

The line of code I added was just :

insim.send(pyinsim.ISP_MST, Msg='(NODE):%d' % car.Node)

Anyway, I downloaded 1.64, ran the installer and now when I try to run any of the example scripts, I'm getting :

InSim Error: Could not init InSim


Mike
================================

My python path =

C:\Python26\Lib;C:\Python26\DLLs;C:\Python26\Lib\lib-tk;E:\Python26\Lib;E:\Pytho
n26\DLLs;E:\Python26\Lib\lib-tk;E:\Python26\Lib\site-packages

(E:\python26 is where pyinsim loaded itself, and e:\python26 is in my path)

Directory of E:\Python26\Lib\site-packages

08/04/2010 10:24 PM <DIR> ..
08/04/2010 10:24 PM <DIR> pyinsim
08/04/2010 10:24 PM <DIR> .
02/01/2010 03:44 PM 292 pyinsim-1.6.4-py2.6.egg-info
1 File(s) 292 bytes

Directory of E:\Python26\Lib\site-packages\pyinsim

08/04/2010 10:24 PM <DIR> ..
08/04/2010 10:24 PM <DIR> .
02/01/2010 03:44 PM 85,035 pyinsim.py
08/04/2010 10:24 PM 98,051 pyinsim.pyc
08/04/2010 10:24 PM 98,051 pyinsim.pyo
02/01/2010 03:39 PM 341,757 _strmanip.py
08/04/2010 10:24 PM 339,955 _strmanip.pyc
08/04/2010 10:24 PM 339,955 _strmanip.pyo
02/01/2010 03:39 PM 940 __init__.py
08/04/2010 10:24 PM 362 __init__.pyc
08/04/2010 10:24 PM 362 __init__.pyo
9 File(s) 1,304,468 bytes

Total Files Listed:
10 File(s) 1,304,760 bytes
5 Dir(s) 120,520,704 bytes free



Quote from DarkTimes :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!


Great Cruise example DarkTimes, a nice way for beginners to learn Python that way
@mstram

Heya, I'm not sure what the problem is. Are you using the examples from the old version with the beta? If so they won't work as the API was changed. There are new examples included with the beta that should be OK. If you're not then I would need to see the code you're trying to run to figure out what the problem is.
Dt,

I'm trying to run any of the examples in the 1.6.4..beta\examples folder.

I'm running python ver 2.6.5

Just get :
InSim Error: Could not init InSim

I haven't installed any third party debuggers / IDE's.

Is there a "native" python "trace" or debug instruction ? .. or any other info I can provide?
You can delete the try/except block in an example, which should give you a stacktrace, but I don't think you will get much more info if it's just a InSim Init message (this is the error that's thrown when no other message is applicable).

You could try this, the simplest pyinsim program:

import pyinsim

insim = pyinsim.InSim()
insim.init('localhost', 29999)

I'm running the examples now and they work fine for me.

Edit: I just installed Python 2.6 to make sure and it's still working fine.

EditEdit: Some Python IDEs do allow debugging, but it depends on which one you are using.
I have it working now.

It appears to have been an issue with the firewall and port 29000 not being open, the error was in the socket initialization.

Also, I can't tell if the code was supposed to have the "raise Error('Could not init InSim')" line indented under the 'except' or not (it wasn't indented).


except socket.error as err:
raise Error(err[1])
raise Error('Could not init InSim')

or


except socket.error as err:
raise Error(err[1])
raise Error('Could not init InSim')

Also, how do you terminate an example program?

Ctr-BRK, CTR-C have no effect, I've been going to the Windows task manager and killing python

It would preferable to add a keypress check routine or something similar for a more elegant exit
No, it shouldn't be indented. It's not in my version. Not sure why that is, maybe your IDE interpreting the spaces wrongly.

I was just thinking of that keypress issue today while I was checking the examples. I might need to think about that. I'm not 100% sure that should be a part of pyinsim itself though, as it's kinda of outside its realm. It's not difficult to add it yourself, but it's a bit too late on a Saturday night for me to code up an example now, but I'll think about it more when I'm sober tomorrow.
Tried running Cruise.py
--------------

Traceback (most recent call last):

File "C:\Python26\Lib\site-packages\pyinsim\pyinsim.py", line 2570, in __recvT
hread
[self.__recv(sock) for sock in socks]

File "C:\Python26\Lib\site-packages\pyinsim\pyinsim.py", line 2591, in __recv
[self.__onPacketData(pdata) for pdata in self.__buffer]

File "C:\Python26\Lib\site-packages\pyinsim\pyinsim.py", line 2608, in __onPac
ketData

if isp: self.event(ptype, packet)

File "C:\Python26\Lib\site-packages\pyinsim\pyinsim.py", line 2475, in event
[callback(self, *args) for callback in self.__callbacks[evt]]

File "cruise.py", line 118, in new_ply
if npl.CName in ncn.vars.cars:

AttributeError: 'IS_NCN' object has no attribute 'vars'
The cruise.py example only works if you're connected to a dedi-server, I think that might be the problem. I guess I should write that somewhere in it.
If you want to use a key to exit the program you can simply do this.

import pyinsim

insim = pyinsim.InSim()
insim.init()

raw_input('Press <ENTER> to exit')

insim.close()

In the current beta this actually causes an exception to be thrown on closing (still closes though ), but I have fixed that in my development version.
Quote from DarkTimes :The cruise.py example only works if you're connected to a dedi-server, I think that might be the problem. I guess I should write that somewhere in it.

Hmm ...... now I'm confused.

Do you mean I have to connect to a server using the multiplayer option dialog? ....

And then run the "local" server?

So that LFS would then be connected to two servers?


"""Example 15: Cruise

A simple cruise server. As you drive you earn cash, which can be spent on buying and selling cars. User data is stored as pickled Python objects in the
data\cruise folder,
The cruise.py script needs to be connected to a dedi-server in order to work. It won't work if you just connect it to a local LFS client. You can download the dedi and run it on your local computer, connect cruise.py to it, then start LFS and connect to the dedi using the Local Network option in Join Specific Host.

It may be possible to run the cruise script on a local copy of LFS, if you open it up and change the constant

HOST_ID = 0

to some other value (not between 0 and 255), such as

HOST_ID = -1

#or

HOST_ID = 256

But I've not tested this and most likely some things will break horribly.
Hi D.t.,

Ok, I'll give that a try.

I had tried running that server before, the instructions are not very clear about where to setup the config info. I tried editing the setup.cfg file, but when I start the server it's ignoring / over writing the settings.

It sure doesn't have as nice an interface for settings as the multiplayer screen in the LFS client.

I'll revisit it , and I saw there was a network "sticky", I'll re-read it, and see what the issue is with it
You could also use this to raw send commands to the console?
I'm not sure what you mean, Dygear.
Quote from mstram :Hi D.t.,

Ok, I'll give that a try.

I had tried running that server before, the instructions are not very clear about where to setup the config info. I tried editing the setup.cfg file, but when I start the server it's ignoring / over writing the settings.

It sure doesn't have as nice an interface for settings as the multiplayer screen in the LFS client.

I'll revisit it , and I saw there was a network "sticky", I'll re-read it, and see what the issue is with it

Yeah it can be a bit tricky the first time, after that it's easy though. Here is a quick guide
  • Download the dedi-server
  • Open the file setup.cfg with your favourite code editor
  • Locate the line that says '//insim=29999' and change it to say '/insim=29999'
  • Find the line '/usemaster=yes' and change it to '/usemaster=no' (optional but good for devlopment)
  • Start the dedi-server by double-clicking the exe
  • Run the cruise.py script from either the command line or from your Python IDE
  • Confirm in the dedi-server window that 'pyCruise' has connected
  • Start LFS, go to Multiplayer, select Join Specific Host, then choose the Local Network tab
  • Set the Host IP address to '127.0.0.1' and the Host port to 63392
  • Click Go in LFS and confirm you have connected to the host
That should work for you, if it does not then I will need more information.

Edit: LFS will overwrite your config if you edit it while the program is running. You need to stop LFS, edit the config, then restart it.
Hi Dt.,

Thanks for the all the info !

It's almost working

The dedicate server says "A new guest is connecting", then the LFS client pops up a message saying "Did not receive driver info", and the ded-serv then says "New Guest failed to connect"

Mike
I don't know the solution to that problem, you might need to ask in the hosts forum.
Quote from DarkTimes :If you want to use a key to exit the program you can simply do this.

import pyinsim

insim = pyinsim.InSim()
insim.init()

raw_input('Press <ENTER> to exit')

insim.close()

In the current beta this actually causes an exception to be thrown on closing (still closes though ), but I have fixed that in my development version.

Quote from Dygear :You could also use this to raw send commands to the console?

Quote from DarkTimes :I'm not sure what you mean, Dygear.

Yeah, sorry that was really out of context, as when I posted that message I had no idea there was another page to this thread.

Anyway, You could use RAW_INPUT to send raw command async to the lfs console after you type in the command, and then when you hit return it would send a packet that would run that command from the host side. It would be like RCON (Remote Console in Counter-Strike terms.)
Ah right, I think I get you. Yeah, you could use it to send commands from the console to LFS.

import pyinsim

print 'Enter command and press return to send, or press CTRL+Z to exit'

insim = pyinsim.InSim()

try:
insim.init('localhost', 29999)

while True:
cmd = raw_input('Enter Command: ')
insim.sendm(cmd) # Send command!

except EOFError:
insim.close()
except pyinsim.Error as err:
print 'InSim Error: %s' % err

Inspired by PRISM I decided to see if I could use pyinsim to create a plugin hosting system. I've been hacking away at it for a few days and it seems to be working quite well, although it's just a prototype. I ended up having to rewrite a bunch of stuff in pyinsim, especially the socket code, so that it nicely supported multiple hosts and also properly integrated with the plugin system.

Here is a brief list of features:
  • Modular plugin system
  • Simple per-plugin data persistence
  • Inter-plugin messaging system
  • Support for multiple hosts
  • Host state management (players, connections etc..)
Creating a plugin

You create a new plugin by creating a new module in the plugins directory. Inside this module you add you plugin classes, which must inherit from the plugin.Plugin base class. Here is an example of the simplest plugin you could create, that simply sends a welcome message to each host that connects.

# hello.py

import plugin

class Hello(plugin.Plugin):
def __init__(self):
plugin.Plugin.__init__(self)

def connected(self, host):
host.send(plugin.ISP_MST, Msg='Hello, %s' % host.name)

The Plugin base class contains a few methods you can override to catch specific events.
  • initialized - The plugin system is initialized, the plugin_map and data members are populated.
  • uninitialized - The plugin system is shutting down and the plugin data is about to be saved to disk.
  • connected - A new host has connected and added to the host_map.
  • disconnected - A host has been lost and removed from the host_map.
The Plugin also contains a bunch of other methods and properties.
  • plugin_map - A dictionary of currently loaded plugins keyed by name
  • host_map - A dictionary of currently connected hosts keyed by name
  • data - A user object you can set that will be available the next time the plugin starts (you can store anything that's serializable, it's a dict by default)
  • bind(), unbind() and isbound() - Packet event binding methods
  • subscribe(), unsubscribe() and notify() - Messaging system for communicating between plugins
  • find_plugin() - Find another plugin by name
You can bind packet events inside a plugin, just like you could with normal pyinsim. Here is a simple example that spectates any driver that goes above 80 Kph.

# speeding.py

import plugin

SPEED_LIMIT = 80 # Kph

class Speeding(plugin.Plugin):
def __init__(self):
plugin.Plugin.__init__(self)
self.bind(plugin.ISP_MCI, self.car_update)

def car_update(self, host, mci):
for car in mci.Info:
if plugin.kph(car.Speed) > SPEED_LIMIT:
npl = host.players[car.PLID]
host.sendm('/spec %s' % npl.PName)
host.sendm('%s ^7spectated for speeding!' % npl.PName)
host.sendm('Do not go above %d Kph!' % SPEED_LIMIT, ucid=npl.UCID)

The host argument represents the host that the packet came from. It has a few methods and properties you can call.
  • conns - The current connection list dictionary
  • players - The current player list dictionary
  • state - The current host state (last STA received)
  • name - The host name (as defined in config.py)
  • hostaddr - A tuple containing the (ip, port) of the host
  • send() - Send a packet to the host
  • sendm() - Send a message to the host (MTC, MST or MSX)
Configuring and starting the app

The configuration can be found in config.py file, which is basically just a python file where you can set various global variables. In here you can configure which hosts you want to connect to, and also which plugin modules get loaded. It's pretty self-explanatory when you see it.

Once you have created some plugins and configured the hosts, you can start the app by double-clicking the 'pyinsim.bat' file in the directory, or with the command 'python app.py'.

As I say it's just a prototype that I hacked together in a couple of days. It'll probably have a few bugs in it, and I'm not 100% happy with the plugin API, so that might change if I continue to work on it, but all in all it seems to be working quite nicely.

Edit: Note you cannot use a ReqI of 255 in your plugins, that's reserved by the system. I may change it to some other number. Not sure.
Attached files
pyinsim3000_0.0.1.zip - 98.3 KB - 392 views
Quote from DarkTimes :
host.send(plugin.ISP_MST, Msg='Hello, %s' % host.name)


I wish PHP had this elegance. Great work as always DarkTimes.
Yeah, Python is a very elegant language. That's one of the reasons I like it so much. Plus, it's just awesome.

Anyway, trying to think up ideas for plugins, I wrote a little swear-filter. It checks each message for a swear-word and kicks the player after giving three warning. You might need to make the actual swear list more realistic.

# !/usr/bin/env python
# swear_filter.py

import plugin
import re

# List of swearwords, in lower-case.
SWEAR_WORDS = ['boobs', 'bum', 'fart', 'knockers', 'poo']
MAX_WARNINGS = 3 # 0 for no warning.
SPLIT_REGEX = re.compile('\W+')

def get_swearwords(msg):
"""Return a list of swearwords in the message."""
words = SPLIT_REGEX.split(msg.lower())
return filter(lambda w: w in SWEAR_WORDS, words)

def contains_swearword(msg):
"""Return true if a message contains a swearword."""
words = SPLIT_REGEX.split(msg.lower())
for word in words:
if word in SWEAR_WORDS:
return True
return False

class SwearFilter(plugin.Plugin):
"""Plugin to handle a swear-filter."""
def __init__(self):
plugin.Plugin.__init__(self)
self.warnings = {} # Store warnings in a dict with the uname as the key.
self.bind(plugin.ISP_MSO, self.message_out)
self.bind(plugin.ISP_CNL, self.connection_left)

def message_out(self, host, mso):
"""Handle MSO message packet."""
if mso.UserType == plugin.MSO_USER:
msg = mso.Msg[mso.TextStart:]
if contains_swearword(msg):
ncn = host.conns[mso.UCID]
self.update_warnings(ncn)
self.check_warnings(host, ncn)

def connection_left(self, host, cnl):
"""Handle CNL packet, delete any warnings which exist for the user."""
ncn = host.conns(cnl.UCID)
if ncn.UName in self.warnings:
del self.warnings[ncn.UName]

def update_warnings(self, ncn):
"""Increment a user's warnings."""
if ncn.UName in self.warnings:
self.warnings[ncn.UName] += 1
else:
self.warnings[ncn.UName] = 1 # Add new warning to dict.

def check_warnings(self, host, ncn):
"""Check if the user has exceeded their warning quota, otherwise send warning messge."""
if self.warnings[ncn.UName] >= MAX_WARNINGS:
host.sendm('^7| %s ^3kicked for swearing' % ncn.PName)
host.sendm('/kick %s' % ncn.UName)
else:
warnings = self.warnings[ncn.UName]
host.sendm('^7| ^3Swear warning (%d of %d)' % (warnings, MAX_WARNINGS), ncn.UCID)

Drop it into a module called 'swear_filter.py' and add a reference to 'plugins.swear_filter' to the PLUGINS config variable.
OK, only one download in the past three days, I guess using a scripting language to control hosts isn't a popular idea.
Quote from DarkTimes :OK, only one download in the past three days, I guess using a scripting language to control hosts isn't a popular idea.

I'm going to tend to disagree, but I guess we shall see.

FGED GREDG RDFGDR GSFDG