The online racing simulator
B-E-A-U-tiful!

/me is desperate to learn Python as soon my Ranking System is finished...
Just wanted to say thanks to DarkTimes for all of his hard work and for making this available to all of us. I certainly appreciate it! Thanks!
Just a short note for an oddity I noticed ('pulled my hair out over') earlier. For some reason calling InSim.Close() doesn't actually seem to close the internal socket, when running in TCP mode. You need to send the packet to close InSim before you try to close the socket, or else nothing seems to happen (and nothing happening is the worst thing when you're trying to figure out a bug).


# Send tiny close to InSim.
tinyClose = Packet(ISP_TINY, SubT=TINY_CLOSE)
insim.SendP(tinyClose)

# Now you can close the socket.
insim.Close()

This may well be a Berkeley sockets issue, and not a Pyinsim or Python problem, but it's different behaviour from that in the .NET Berkeley sockets implementation that I'm used to. I'm not sure what's going on... but it's likely I've run out of brains...

I may roll a workaround into the next release, if I can get around to releasing it.

Edit: This is fixed in Pyinsim 0.1.5 and above.
Hey!

First I have to praise Darktimes for his great work on PyInsim!

My actual problem:
I have a button:
InSim.SendP(self, Packet(ISP_BTN, ReqI=1, ClickID=cid, UCID=ucid,
BStyle=ISB_DARK | ISB_RIGHT,
W=cw, H=ch, T=cy, L=cx, Text=carbtntext))

[ReqI is the same for all my buttons since I dont know what it does...]

Now I added ISP_BTC to the list of packet handlers, but the handler doesnt get triggered once I click the button (in "show connections" mode to have a cursor).
What am I doing wrong?
Quote from Tanuva :I have a button:
InSim.sendP(self, Packet(ISP_BTN, ReqI=1, ClickID=cid, UCID=ucid,
BStyle=ISB_DARK | ISB_RIGHT,
W=cw, H=ch, T=cy, L=cx, Text=carbtntext))

Now I added ISP_BTC to the list of packet handlers, but the handler doesnt get triggered once I click the button (in "show connections" mode to have a cursor).
What am I doing wrong?

It looks like you need to add the flag ISB_CLICK to the BStyles byte.
InSim.sendP(self, Packet(ISP_BTN, ReqI=1, ClickID=cid, UCID=ucid,
BStyle=ISB_DARK | ISB_RIGHT | [B]ISB_CLICK[/B],
W=cw, H=ch, T=cy, L=cx, Text=carbtntext))

Now when the button is clicked, InSim will send back an IS_BTC packet which will be picked up by the packet handler.
Of course! I knew I read something about that in InSim.txt, but I couldn't refind it. Thank you!
Uploaded version 0.1.5 to the first post. It contains a few fixes and changes, and please note that it breaks comptibility with previous versions.

Changes:
  • Fix: Fixed 'bad file description' error on attempted reconnects.
  • Fix: Fixed bug with InSim not closing when calling InSim.Close() in TCP mode.
  • Fix: Changed InSim.Run() documentation to make it clear it's designed to only be used when running Pyinsim from a console application.

  • Change: Added ISP_CPP Camera Position Packet, which means all InSim packets are now complete.
  • Change: Renamed InSim.RegisterPackets() and InSim.UnregisterPackets() to InSim.Bind() and InSim.Unbind() respectively. This is a breaking change.
  • Change: Added LFSTime class, for handling time operations.
  • Change: Increased internal buffer size default to 1024
  • Change: Added InSim.ConnectionLost() event, which is called when the InSim connection is lost.
Note: Pyinsim does not currently yet support Vectors as a data-type, so a small work-around is used. For instance, instead of the Pos attribute of the CPP packet being a vector, it is in fact split into three seperate attributes, which are named Pos1, Pos2 and Pos3, each of them containing an int.

Also note that instead of distributing four separate downloads, I have combined them all into one single package. This file contains the windows installer, the source distribution, license, readme and HTML documentation, as well as the template file.
DarkTimes!


Thank you!!!!!!


Ok... I took me some few time today and tried to understand python and your lib a bit...

Well I think I already failed at the start

Ok... As I told you in the PM I sent you I am also intersted in hanling a rolling start and... tataaaaaaaa... you already coded something like that!

But the Problem I have is that any speeding of 1mph causes a spec.

So I tried to change your code a bit. I wanted that a personal rcm appears to the driver who speeded (I dunno how to code buttons yet), then the timer should run, rcm should be cleared by rcc and then if the car still is faster than the limit a spec should follow...

This is my try:

elif ToKph(car["Speed"]) > SPEED_LIMIT:
# Player broke the speed-limit, spec them.
SendMessage("/rcm " + GET_SLOWER)
SendMessage("/rcm_ply " + players[car["PLID"]][0]["PName"])
BlockingTimer(MSG_TIMEOUT)
SendMessage("/rcc_ply " + players[car["PLID"]][0]["PName"])
players[car["Speed"]] = 0
if ToKph(car["Speed"]) > SPEED_LIMIT:
SendMessage("/spec " + players[car["PLID"]][0]["PName"])
SendMessage(SPEEDING_MSG)

But I realized 2 big problems with it:
1.) rcm_ply does not work with Nickname and changing "PName" to "UName" gives me an error...
2.) I become sent to spec anyway even if I slow down under the limit...

I seems to me that the 1st speed I got the message for speeding for is stored somehow and at the "if" it still is over the limit...

So I tryed to set the Speed to 0 manually which seems not to work too...

An other problem I noticed is that there seems to be an endles loop after speeding... because I am not able to join the race later - I always become to spec again as soon as I join...

Any clue?

If I (or we) manage to get this to work my next step will be to create a command like "!rollingstarton" and "!rollingstartoff" to be able to choose wether there will be a rolling start or not. The over-next step should be using a config file and then creating a exe...
Quote from Crady :1.) rcm_ply does not work with Nickname and changing "PName" to "UName" gives me an error...

Using the nickname should work. Not sure why it doesn't. It works fine for me.

Quote :
2.) I become sent to spec anyway even if I slow down under the limit...

The example I posted is very simple, if you go across the speed limit at all then you are just spectated immediately. If you wanted to adapt it to give a warning to slow down, you would need to setup some sort of timer that would start when the player began speeding, then after X amount of seconds had passed it would check the speed again, and if you were still over the limit you would get spectated.

Edit: The example program is badly written, I've uploaded a better version now.
I've completely rewritten the original RollingStarts program and I've updated the original post. The new version is written in a much clearer way and I tested it a few times to check it works without bugs. It may still have a couple, I don't know. It still works the same way as the previous version mind you.

http://www.lfsforum.net/showthread.php?p=774956#post774956

In other news: Update version 0.1.6 in the first post.

Changes
  • Updated PyinsimTemplate.py to add support for taking over cars and for players being renamed. Also added connection lost event handler.
  • Added ToUnicode(str, default) function which converts a LFS string to unicode, and handles converting all the character encodings and escape chars. Note this is still being tested, so might be a little flaky.
  • Added keepAlive boolean param to InSim(connType, keepAlive) contrsuctor, which allows you to set if InSim responds to keep alive packet automatically.
  • Added CheckPort(ipStr) and CheckIP(portStr) functions, to make it easy to validate IP addresses and port number strings.
  • Added ParseCommand(mso, prefix, sansPrefix), which parses a command from an MSO packet and returns a tuple.
  • Added weather and wind enums.
  • Added flags enum to check ISP_FLG attributes.
  • Added car info flags
Thank you, DarkTime, great one!

I will test your rewritten Code this afternoon and will report! But yet I got some worries in mind: What will happen if the racer at Pole spinns out although he obtained speedlimit? Do all other drivers go to spec then because they "overtake" ?? Well although it is just a "Demo Code" I would like to stay and improve on it... - I would say that spec could be changed with a drive through penalty and the formation lap Funktions itself should be switchable to on/off via a command which only can be applied by an Amdin...

Secondary I have a suggestion to python / Pyinsim itself:

What do you think about developing a good insim application together in this forum to 1st create a usefull app, 2nd to show the possibilities of Python and 3rd help ppl learning Python (espeically Pyinsim)...
Quote from Crady :Thank you, DarkTime, great one!

I will test your rewritten Code this afternoon and will report! But yet I got some worries in mind: What will happen if the racer at Pole spinns out although he obtained speedlimit? Do all other drivers go to spec then because they "overtake" ?? Well although it is just a "Demo Code" I would like to stay and improve on it... - I would say that spec could be changed with a drive through penalty and the formation lap Funktions itself should be switchable to on/off via a command which only can be applied by an Amdin...

Yes, in its current form it's very harsh on mistakes, as if you pass another driver at all it specs you. You could easily mod it to give a penalty instead.

def PenalisePlayer(pName):
SendMessage('/p_dt %s' % (pName))

Which would give a drive-through instead, which an admin could clear if they decided it was unfair.

Quote :What do you think about developing a good insim application together in this forum to 1st create a usefull app, 2nd to show the possibilities of Python and 3rd help ppl learning Python (espeically Pyinsim)...

Sorry, but I'm quite busy with some other projects at the moment, or I should be busy with them, if I wasn't such a slacker. I will help people using Pyinsim all I can, but I can't work on any outside projects specifically at the moment.
Quote :Sorry, but I'm quite busy with some other projects at the moment, or I should be busy with them, if I wasn't such a slacker. I will help people using Pyinsim all I can, but I can't work on any outside projects specifically at the moment.

I really can imagine that you are very busy - as I am too...

But I now decided to change and spare my projects and believe me, I bought a Video2Brain DVD for Python beginners and a very big book "Python the complete manual"... I guess after watching the video and reading the 1st 100 pages in this book I will be able to understand it more clearly!

My big problem is the LFS insim... I never spent time in that and to be honestly I do not understand anything in the manual which can be found in the doc folder... But thats why you made this great lib... Now I only need to know what I can get from LFS and how to use it

My far away aim is create a webapplication which makes it via user accounts possible to manage the lapper config more easy than scripting a config file... I mean adding/removing/changing autoactions, adding/removing user rights, editing the swear filter, editing the ban list, synchronizing the ban list for several servers of a team... etc...

Well hard dreams.... but lets see, what the future brings
Hi...

After reading a bit in the Pyton book I bought I must say that I seem to be able to do some simple scripts...

But I want to create some insim apps... And this still is my problem:

To configure an amount of Servers I want to create an app wihich asks me "Which Admin Commad?" for Security it also should ask for the Admin Pass.

Then it should connect to to all IPs / Ports entered in a List, execute the commad, end exit insim...

But my problem is how to close the insim after the command and how to open another after this?

Or is it possible to connect to more than one Server at once?

This is my try with the use of your template:

#
# Copyright 2008 Alex McBride.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the Lesser General Public License (LGPL) as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

import Pyinsim
import sys


# Init globals
insim = Pyinsim.InSim(Pyinsim.INSIM_TCP)
connections = {}
players = {}
Server = ['192.168.2.104', '127.0.0.1']
Port = [29999, 29998]
# Helper functions.
def SendMessage(msg):
"""Send message to LFS."""
if len(msg) > 64:
insim.SendP(Pyinsim.Packet(Pyinsim.ISP_MSX, Msg=msg))
else:
insim.SendP(Pyinsim.Packet(Pyinsim.ISP_MST, Msg=msg))


def SendMessageConn(msg, ucid=0, plid=0):
"""Send message to a specific connection or player."""
insim.SendP(Pyinsim.Packet(Pyinsim.ISP_MTC, Msg=msg, UCID=ucid, PLID=plid))


#def RequestPlayersConns():
# """Request all players and connections to be sent."""
# insim.SendP(Pyinsim.Packet(Pyinsim.ISP_TINY, ReqI=1, SubT=TINY_NCN))
# insim.SendP(Pyinsim.Packet(Pyinsim.ISP_TINY, ReqI=1, SubT=TINY_NPL))


def GetConnection(ucid):
"""Get connection from UCID."""
return connections[ucid]


def GetPlayer(plid):
"""Get player from PLID."""
return players[plid]


def GetPlayerFromUcid(ucid):
"""Get player from UCID."""
for player in players.itervalues():
if player['UCID'] == ucid:
return player
return None


# TODO: Add more helper functions.


# Packet received events
def VersionCheck(ver):
"""Check the version."""
if ver['InSimVer'] != Pyinsim.INSIM_VERSION:
print 'Invalid InSim version detected.'
sys.exit(0)


def ConnectionJoined(ncn):
"""Add connection to connections dictionary."""
connections[ncn['UCID']] = ncn


def ConnectionLeft(cnl):
"""Delete connection from connections dictionary."""
del connections[cnl['UCID']]


def ConnectionRenamed(cpr):
"""Rename player in connections and players lists."""
connection = GetConnection(cpr['UCID'])
connection['PName'] = cpr['PName']
player = GetPlayerFromUcid(cpr['UCID'])
player['PName'] = cpr['PName']
player['Plate'] = cpr['Plate']


def PlayerJoined(npl):
"""Add player to players dictionary."""
players[npl['PLID']] = npl


def PlayerLeft(pll):
"""Delete player from players dictionary."""
del players[pll['PLID']]


def TookOverCar(toc):
"""Change UCID for player."""
player = GetPlayer(toc['PLID'])
player['UCID'] = toc['NewUCID']


# TODO: Add more packet event handlers.


# Bind events.
insim.Bind({Pyinsim.ISP_VER: VersionCheck,
Pyinsim.ISP_NCN: ConnectionJoined,
Pyinsim.ISP_CNL: ConnectionLeft,
Pyinsim.ISP_NPL: PlayerJoined,
Pyinsim.ISP_PLL: PlayerLeft,
Pyinsim.ISP_CPR: ConnectionRenamed,
Pyinsim.ISP_TOC: TookOverCar})


# Connection lost.
def ConnectionLost():
print 'InSim connection lost.'
sys.exit(0)

insim.ConnectionLost(ConnectionLost)

# Ask for Input
Command = str(raw_input('Command?: '))
Adminpass = str(raw_input('Admin Pass?: '))


# Connect to InSim.

try:
insim.Connect(Server[0], Port[0])
except Pyinsim.socket.error, (ex):
print 'Connection to InSim failed: %s' % (ex.args[1])
sys.exit(0)
else:
# Initailise InSim and request players/connections.
insim.SendP(Pyinsim.Packet(Pyinsim.ISP_ISI, Admin=Adminpass,
IName='^3Pyinsim', ReqI=1))

# send message
SendMessage(Command)

# Keep program thread alive.
insim.Run()
finally:
insim.Close

It's nice you are learning some programming. It's an addictive hobby.

Yes, connecting to multiple servers is possible, although it does add a few complications. The important thing to note however is that each instance of InSim() can only deal with a single server connection, so you would need to create a new InSim() object for each server you wanted to connect to. You could store them in a Python list or tuple.

This is a rough example of how might do this (it's totally untested). Note: I made the Server and Port lists a dictionary called 'hosts', and created a new list called 'sockets', just as it seemed a little neater. I loop through the list of hosts, create a new InSim connection for each server, and add each connected server to our sockets list.


# Store hosts and ports as a dictionary (dict).
hosts = {'192.168.2.104': 29999, '127.0.0.1': 29998}

# Create a list to store each InSim connection.
sockets = []

# Loop over the hosts dictionary.
for host, port in hosts.iteritems():

# Create new InSim object.
insim = Pyinsim.InSim()

# Connect to this server.
insim.Connect(host, port)

# Add connected InSim object to our sockets list.
sockets.append(insim)

Then if you wanted to access a certain server, you could do something like this.


if sockets[0].Connected == True:
print 'Socket 0 connected' # First server in sockets list connected.

if sockets[1].Connected == True:
print 'Socket 1 connected' # Second server in sockets list connected.

Or you could loop through each server:

for socket in sockets:
if socket.Connected == True:
print 'Socket connected!'

I hope that make some sense to you.

Maybe if you explain what it is you are trying to do I can provide a more detailed example, as there a quite a few ways you could go about doing this.
Hmm... wrong OS booted now... I can take a closer look on it this afternoon - after the DTM finale

Well exactly it should be a way in which an Admin is able to change a setting to every of his Servers once without connecting to all of them...

e.g. You have 5 server and want to change all of them to have 30 minutes quali. Then you have to connect to all 5 and have to type /qual=30 ... Or you have a guy you don´t want on your Servers, you need to visit all of your Servers and type /ban xyz xdays ... etc...

In this case I just want to create small app doing this at once... If I get this to work I would like to create a GUI... I am not sure if I should create a windows GUI or buttons in LFS...
Ok... I tryed it and got some Problems...

I in only can send the a Message if I am loggend in as Admin and if ther is the command : insim.Run() or in ,y example socket.Run()

But this command stops the program from connecting to the next server... Here is my try so far:

# Ask for Input
Command = str(raw_input('Command?: '))
Adminpass = str(raw_input('Admin Pass?: '))

# Loop over the hosts dictionary.
for host, port in hosts.iteritems():

# Create new InSim object.
insim = Pyinsim.InSim()

# Connect to this server.
insim.Connect(host, port)

# Add connected InSim object to our sockets list.
sockets.append(insim)


# Loop over the sockets
for socket in sockets:
if socket.Connected == True:
print 'connected'
socket.SendP(Pyinsim.Packet(Pyinsim.ISP_ISI, Admin=Adminpass,
IName='^3XXX', ReqI=1))
SendMessage(Command)

socket.Run()

Whenever you call InSim.Run() it effectively pauses your program at that point, and no code that comes after it will be executed until the InSim connection closes. That means you need to put it right at the end of your program, something like this.
Command = str(raw_input('Command?: '))
Adminpass = str(raw_input('Admin Pass?: '))

for host, port in hosts.iteritems():
insim = Pyinsim.InSim()
insim.Connect(host, port)
sockets.append(insim)

for socket in sockets:
if socket.Connected == True:
print 'connected'
socket.SendP(Pyinsim.Packet(Pyinsim.ISP_ISI, Admin=Adminpass,
IName='^3XXX', ReqI=1))
SendMessage(Command)

# TODO: Write rest of program.

# END OF PROGRAM
for socket in sockets:
socket.Run()

Yes, thats it!

Now it works... Thank you!
ok, ok... it only seems to work on my testing place at home with 2 Servers...

Now I found another prolem:

The "real" servers are hosted at 500Servers. I am talking about 4 Server where always 2 Server have the same IP - just different ports...

If I now start the Program it only connects to 2 Server (to one for each IP) instead of all 4..

my hosts are configured like that:

hosts = {'192.168.2.104': 29999, '192.168.2.104': 2998, '127.0.0.1':2997, '127.0.0.1': 29996}

Any Idea? Perhaps I create a 2 Dictionaries? and then loop over 2 Dictionaries?
Ok... I solved this problem:

The ports are unique. So I changed the order in the dictionary to {port: host} and it works...

Thank you!

But now there encounters an other question:

By using this technique: Is it possible to use your RollingStart and FlagMessages program - or even other progrmas to work on more than one Server at time? I mean if I let the insim.connect loop over 4 or more server Do I have a rolling start option or a displayed blue/yellow flag message at all Server or do you think the program will become messed?

If I will work I would recommend that you use this option to your next programs. So teams and leagues are able to use them by only starting one app instead of one for each server..
DarkTimes,

Have been some busy these days... But now I have time to go further

Ok, as I told you everything works as mentioned... But now I again have one Question:

Is it possible to do the "SendMessage(Command)" to a special time or lets say every 10 Minutes?

In this way I could make this program sending some "news" every 10 Minutes as message or a "Time to sleep" warning a 00:00h every day...

like this:

for socket in sockets:
if socket.Connected == True:
print 'connected'
socket.SendP(Pyinsim.Packet(Pyinsim.ISP_ISI, Admin=Adminpass,
IName='^3XXX', ReqI=1))
SendMessage('/msg ' + News) #every 10 Minutes
SendMessage('/mag Time to sleep now') #at 00:00h every day

# TODO: Write rest of program.

# END OF PROGRAM
for socket in sockets:
socket.Run()

Yes, it's possible to do this. The best way would be to use Python's built-in threading.timer class, which allows you to set up a timer which calls a function or method after a certain number of seconds have elapsed.


INTERVAL = 600 # Number of seconds in 10 minutes.

def timer_event():
# Timer event fired, send message.
SendMessage('/msg {0}' % (News))

# Start timer again.
timer = threading.Timer(INTERVAL, timer_event)
timer.start()

# Start timer.
timer = threading.Timer(INTERVAL, timer_event)
timer.start()

That's a rough example of how you might do it, you can look at the RollingStarts example I posted to see a timer in use, it uses a timer to control how long RCM messages appear on the screen.

If you wanted a message to be sent at a certain time each day, you could setup a timer that elapsed every second or so, and checked to see what the current time is. Then you could send a message


if current_time == message_send_time:
SendMessage(msg)

I'd need to look up the documentation for Python to see what classes or functions you would need for this, but you should be able to find them in Pythons time and threading.timer modules, if you look them up in the standard library yourself.

HTH
Hi, DarkTims!

Again thanks

Well the timer in this way seems to go in an endless loop and crash the whole program when executed in IDLE... So I changed it pit and it now closes all InSim connecrions one after another after 10 Seconds...

So I am able to use Windows Scedule Service to open the program when I need it...

Because of this I only took a short view on the current_time thing... But I saw that I can get the current system time by including Year, Date, Seconds and Miliseconds as a list... So I first have to strip out only the thime etc.... Well this is done by the Windows Scedule Service too
Wow, I kind of stopped watching this thread, glad everyone is still interested in this.

I have a question about buttons... I've had great success creating individual buttons and making them do all sorts of stuff, but I've hit a brick wall.

Is there any way to make buttons overlap? I want to do something like the menus in CTRA or Alrio where there are a bunch of buttons sitting within a "panel", but I can't seem to make it work. I figured the way to do it was make a big button with no text in it and overlay it with more buttons, but then the underlying button just doesn't display.

Any ideas?

** Edit **
Please ignore, I was using the same ClickID for the buttons meant to make this work... problem solved. Going to keep this here in case other idiots like myself stumble on the same problem.

FGED GREDG RDFGDR GSFDG