The online racing simulator
Hey,

I'm just starting with Python and it may be a foolish question but here we go.

I'm using this:
threading.Thread(target=yellowflag).start()

to create a thread, so I can get a button that changes the text every second indicating that safety will still be on track for another lap (just like F1 Safety Car lights). The problem is, when I use CTRL + C on Putty, it just won't stop the thread, I have tried things like try/catch but no success. Have you got any idea for this? It's important for now to stop the thread when I press CTRL + C because I'm still developing so I change stuff all the time.

EDIT: Posting the function so there may be something wrong with it, also.
YellowFlag = 0

def yellowflag():
flashing = 0

while(YellowFlag):
if(flashing == 0):
texto = chr(46) + ' ' + chr(46)
remover_botao(255, 0)
insim.send(pyinsim.ISP_BTN, ReqI=255, UCID=255, ClickID=0, BStyle=32+1, L=90, T=25, W=20, H=10, Text=texto)
flashing = 1
sleep(1)
else:
texto = chr(46) + ' ' + chr(46) + ' ' + chr(46)
remover_botao(255, 0)
insim.send(pyinsim.ISP_BTN, ReqI=255, UCID=255, ClickID=0, BStyle=32+1, L=90, T=25, W=20, H=10, Text=texto)
flashing = 0
sleep(1)

PS: No need to call global YellowFlags as it has been called already before I start the thread.
Quote from RenvoN :
def yellowflag():
flashing = 0

while(YellowFlag):
if(flashing == 0):
texto = chr(46) + ' ' + chr(46)
remover_botao(255, 0)
insim.send(pyinsim.ISP_BTN, ReqI=255, UCID=255, ClickID=0, BStyle=32+1, L=90, T=25, W=20, H=10, Text=texto)
flashing = 1
sleep(1)
else:
texto = chr(46) + ' ' + chr(46) + ' ' + chr(46)
remover_botao(255, 0)
insim.send(pyinsim.ISP_BTN, ReqI=255, UCID=255, ClickID=0, BStyle=32+1, L=90, T=25, W=20, H=10, Text=texto)
flashing = 0
sleep(1)


Where in your code is the YellowFlag variable being set to false? You will need to set YellowFlag to false at some point, otherwise the code will enter an infinite loop, which would indeed stop the thread from ever exiting. I would double-check your code to make sure that variable is being correctly unset.

Additionally I would not suggest using a thread for this, a better idea would be to use a timer set to elapse every second. Check out the threading.Timer class.
I fixed the messy looking error on keyboard interrupt and pushed the change onto the repository.
Been messing with the cruise script some more (done quite a bit actually) and I've come to something I can't quite solve.
I'm working on some admin commands (!kick, !ban and such) along with reasons. I'm trying to get it to display the reason to the user. codes work as !kick user [reason is the rest of the args]
reason = args[1:]
insim.sendm('^3| ^1Spectated: ^7%s' % reason, other_ncn.UCID)
insim.sendm('/spec %s' % args[0])

(using the wonderful get_ncn_by_uname you made for me). Problem is, the reason formatting is weird.
| Spectated: ['this', 'is', 'a', 'test']

Is what I see. I just want it to say "this is a test"

Any ideas?
The command parsing code splits the command into a list of words, so for instance the command:

!give DarkTimes 2000

Would become a list like:

args = ['!give', 'DarkTimes', 2000]

To turn the list back into a string you can use str.join().

reason = ' '.join(args[1:])

Thanks mate. Works like a charm

One more question. I've added an !tow, and this is also a problem while pitting. I can't quite think of how to fix it, but when you use the !tow (/pitlane) or pit, because the location jumps from where you were on track to the pits, the distance shoots up quite a bit (I've had it go up over a kilometer and a half on WE1R). How can I fix it so when a user users !tow or pits/specs, the distance won't shoot up?
You would need to suspend then resume the distance update while you are sending them to the pits. Add a flag to the user vars, something like is_pitting, then before you send the pit command set that to true. Then when updating the distance check to see if that flag is set, if it is then don't add any distance for them. Once they leave the pits you can set the flag back to false.
Thanks mate. I'll have a go at it.
Hi.

I'm really struggling to understand how receiving packets work. I would be interested in timing (lap-and split times and positions). I'd appreciate an example on how to retrieve that data if possible.
OK, I've written a small example to show how to handle laps, splits and positions. Laps are contained in the IS_LAP packet, splits in the IS_SPX packet, and positions are included in the IS_NLP packet.

This example is fairly simple - it tracks each player and prints out the name, position, lap time and split times whenever they complete a lap. I've tried to comment each line to explain what it does.

Just to enter a shameless plug you can also use my other program InSimSniffer to watch the packets as they arrive, which can help a lot in understanding how it all fits together.

import pyinsim

# Dict to store players.
players = {}


def insim_multiplayer(insim, ism):
# Whenever we join a host we request the player list to be sent.
insim.send(pyinsim.ISP_TINY, ReqI=1, SubT=pyinsim.TINY_NPL)


def new_player(insim, npl):
# Create class variables on NPL packet to store current
# position and current splits.
npl.position = 0
npl.splits = []

# Add NPL packet to players dict.
players[npl.PLID] = npl


def player_left(insim, pll):
# Delete player from dict.
del players[pll.PLID]


def split_completed(insim, spx):
# get player from dict.
npl = players[spx.PLID]

# add new split.
npl.splits.append(spx.STime)


def node_lap(insim, nlp):
# Loop through each car in the packet.
for car in nlp.Info:
# Get player for this car.
npl = players.get(car.PLID)

# Sometimes, at the start of a race, you can get NLP
# packets before the player list has been received.
if npl:
# Store current race position.
npl.position = car.Position


def lap_completed(insim, lap):
# Get player from dict.
npl = players[lap.PLID]

print 'Player: %s' % pyinsim.stripcols(npl.PName)
print 'Lap: %s' % lap.LapsDone
print 'Position: %d' % npl.position
print 'Time: %s' % pyinsim.timestr(lap.LTime)

# Google list comprehension if you don't understand this line :p
print 'Splits: %s' % ', '.join([pyinsim.timestr(s) for s in npl.splits])

# Blank line.
print ''

# Reset splits so they're ready for next lap.
npl.splits = []


if __name__ == '__main__':
# Create new InSim connection object.
insim = pyinsim.insim('127.0.0.1', 29999, Admin='', Flags=pyinsim.ISF_NLP, Interval=500)

# Bind events.
insim.bind(pyinsim.ISP_ISM, insim_multiplayer)
insim.bind(pyinsim.ISP_NPL, new_player)
insim.bind(pyinsim.ISP_PLL, player_left)
insim.bind(pyinsim.ISP_LAP, lap_completed)
insim.bind(pyinsim.ISP_SPX, split_completed)
insim.bind(pyinsim.ISP_NLP, node_lap)

# Request host packet on connecting.
insim.send(pyinsim.ISP_TINY, ReqI=1, SubT=pyinsim.TINY_ISM)

# 3, 2, 1 GO!
pyinsim.run()

Awesome

That helps a lot. I get confused when I see a bunch of functions with operands that don't exist...
One (well two related) last question(s) for now :P

How might I go about changing codes so the color doensn't matter? Also, what would I have to do so the wonderful get_ncn_by_uname isn't case sensetive? eg !give learjet45 1000 as well as !give Learjet45 1000 working. I'm thinking something along the lines of storing usernames as lowercase in the connections list/library thing, then use .lower() on the argument string from commands that use it? Or is there a better way to go about doing things?
I don't remember that function I wrote, but imagine it would be something like this...

def get_ncn_by_uname(uname):
uname = uname.lower()
for ncn in connections.values:
if ncn.UName.lower() == uname:
return ncn
return None

Not exactly :P
Here is what I've used for quite a bit of the stuff I've done :P

def get_ncn_by_uname(uname):
ncn = filter(lambda n: n.UName == uname, connections.values())
if ncn:
return ncn[0]
return None

Edit: and one more thing. I'm working on my issue with pitting and what not. However, I cannot seem to figure out how to get the connection info for the player who pitted. I need to define ncn so I can change the ncn.vars.pitting value, but I'm not sure how to go about doing that with the PLP packet.
Quote from learjet45 :Not exactly :P
Here is what I've used for quite a bit of the stuff I've done :P

def get_ncn_by_uname(uname):
ncn = filter(lambda n: n.UName == uname, connections.values())
if ncn:
return ncn[0]
return None


That function basically does the same thing in a different way, even then modifying it to be case-insensitive uses the same principle.


def get_ncn_by_uname(uname):
[b]uname = uname.lower()[/b]
ncn = filter(lambda n: [b]n.UName.lower() == uname[/b], connections.values())
if ncn:
return ncn[0]
return None

Quote :Edit: and one more thing. I'm working on my issue with pitting and what not. However, I cannot seem to figure out how to get the connection info for the player who pitted. I need to define ncn so I can change the ncn.vars.pitting value, but I'm not sure how to go about doing that with the PLP packet.

You need to get the NPL for that packet, then use the UCID in the NPL to get the NCN.

def player_left_pits(insim, plp):
npl = players[plp.PLID]
ncn = connections[npl.UCID]

# Do something with ncn...

I feel like I'm being a nussiance

Thanks for the help with that. Working like a charm now. New problem though

I can't get a !location command to work to save my life. I've tried everything I can think of, no dice.
What is !location supposed to do?

You're just lucky I don't believe in Christmas.
Haha. I completely forgot to mention that :P

Just show x, y, and z coords. Heck I'd even be happy with just x and y. But I cant seem to get anything working.

Edit: Another weird error, and it doesn't always seem to happen. I've had it happen on two separate occasions now. I'm using
insim.sendm('/msg message')

to send a message, but it's being a bit annoying. It's an !onduty command for the cop system I'm working on. Theres both !onduty and !onduty [username]. I can use !onduty for myself fine, and also !onduty user1, but it seems as though when I try it with a different user (eg !onduty user2), lfs server tells me:

IS_MSX - for commands use IS_MST

This also happened at one point with the bonus system I was working on, but I decided to just ignore it then because it for the most part worked fine.
Ah, this is a small bug, but also it sounds like you are sending a command that is too long. The maximum length a command can be is 64 characters, as your command is longer than that the sendm method is sending a MSX instead, which doesn't support commands.
Ah alright. Any ideas on !location?

Oh, and yet another thing. I can't seem to find anything to distinguish AI from regular players with InsimSniffer, and I'm trying to code in AI detection so people can't use AI.
OK - here is an example of making a !location command.

import pyinsim

players = {}

def npl(insim, npl):
# Create class variable on NPL packet to store current XYZ.
npl.xyz = [0,0,0]
players[npl.PLID] = npl

def pll(insim, pll):
del players[pll.PLID]

def ism(insim, ism):
#Request player list.
insim.send(pyinsim.ISP_TINY, ReqI=1, SubT=pyinsim.TINY_NPL)

def mso(insim, mso):
# Parse command.
if mso.UserType == pyinsim.MSO_PREFIX:
msg = mso.Msg[mso.TextStart:]
if msg == '!location':
# Send player's XYZ.
npl = players[mso.PLID]
insim.sendm('X: %d Y: %d Z: %d' % (npl.xyz[0], npl.xyz[1], npl.xyz[2]), ucid=mso.UCID)

def mci(insim, mci):
for car in mci.Info:
npl = players.get(car.PLID)
if npl:
# Set XYZ (convert LFS coords to meters)
npl.xyz = [pyinsim.length(car.X), pyinsim.length(car.Y), pyinsim.length(car.Z)]

insim = pyinsim.insim('127.0.0.1', 29999, Prefix='!', Flags=pyinsim.ISF_MCI, Interval=500)
insim.bind(pyinsim.ISP_NPL, npl)
insim.bind(pyinsim.ISP_PLL, pll)
insim.bind(pyinsim.ISP_ISM, ism)
insim.bind(pyinsim.ISP_MSO, mso)
insim.bind(pyinsim.ISP_MCI, mci)
insim.send(pyinsim.ISP_TINY, ReqI=1, SubT=pyinsim.TINY_ISM)
pyinsim.run()

We store a list of each NPL packet and create a new property called 'xyz', then whenever an MCI packet is received we update the 'xyz'. After that all we need to do is output the coords whenever someone types the command !location.
Quote from learjet45 :Oh, and yet another thing. I can't seem to find anything to distinguish AI from regular players with InsimSniffer, and I'm trying to code in AI detection so people can't use AI.

Sorry didn't see your post before. You can use the PType property of the NPL packet, it contains various bit flags about the player type. You're correct this information is not easily accessible using InSimSniffer, I'll put it on the todo list.

# byte PType; // bit 0 : female / bit 1 : AI / bit 2 : remote

def npl(insim, npl):
if npl.PType & 2:
print '%s is an AI!' % npl.PName

Heya I updated my little cruise script example. I fixed the UDP bug in pyinsim a long time ago, so I re-enabled UDP updates for MCI packets. I also added welcome message that's displayed to each player when they connect. You might want to update this to change the web site URL and remove the swearing.
Holy triple post Batman!
Thanks for the !location help! That will make life a bit easier :P
I was thinking it was the PType, but then I saw that it wasn't always the same so I wasn't sure.
And thanks for the updated script. I'll work on updating the thing later :P I've done quite a bit of coding (gone from about 250 lines of code in the example you wrote to over 1000), and I can't just copy any paste :P Thanks for all the help!
You should use a revision control system, such as Mercurial. That way you can just merge my update with your own script and it will apply the new changes automatically. I can convert my update to a source patch if you need.

FGED GREDG RDFGDR GSFDG