The online racing simulator
Searching in All forums
(921 results)
DarkTimes
S2 licensed
I wrote a small example program to show how to create a InSim button app using a Finite-State Machine. I got the inspiration when I was writing a simple game and saw how effective a FSM is for this kind of thing.

In this example each connection on the host is given their own ScreenManager, that manages what screen that player is currently being shown. Each screen handles displaying and deleting all its own buttons, and also all its own internal logic.

This example allows you to flip backwards and forwards between a welcome screen and an options screen.

Well anyway, it's pretty heavily commented.

import pyinsim

# Class to manage what screen is currently being shown to a
# player. Each connection on the host has their own copy of
# the ScreenManager.
class ScreenManager(object):
def __init__(self, insim, ncn):
self.insim = insim # Copy of the InSim connection.
self.ncn = ncn # Copy of the players connection packet.
self.current_screen = None # The currently loaded screen for this player.

# Transition this player to the next screen.
def transition(self, screen):
# First tell the current screen (if any) to unload its buttons.
if self.current_screen:
self.current_screen.unload_buttons()

# Set the screen the player is currently viewing.
self.current_screen = screen

# A few helpful variables to have in the screen.
self.current_screen.screen_manager = self
self.current_screen.insim = self.insim
self.current_screen.ncn = self.ncn

# Tell the new screen to send its buttons to LFS.
self.current_screen.load_buttons()

def button_click(self, btc):
# Pass on the button click event to the current screen.
self.current_screen.button_click(btc)

# TODO: Implement more packet handlers. Should define handlers
# for any packets the screens may need to handle, just like
# button_click event.

# Abstract class used to define a screen.
class Screen(object):
def __init__(self):
self.screen_manager = None
self.insim = None
self.ncn = None

# Called when the screen is being shown, used to send
# all buttons to be displayed on the screen.
def load_buttons(self):
pass

# Called when the screen is being hidden, used to delete
# any buttons that were sent.
def unload_buttons(self):
pass

# Called whenever a BTC packet is received. Can extend this class
# to handle any other packet events the derived screens might need.
def button_click(self, btc):
pass

# Example welcome screen, shown to players when they first connect
# to the host.
class WelcomeScreen(Screen):
def __init__(self):
Screen.__init__(self)

def load_buttons(self):
# Send buttons when screen first loaded.
self.insim.send(pyinsim.ISP_BTN,
ReqI=1,
UCID=self.ncn.UCID,
ClickID=1,
BStyle=pyinsim.ISB_DARK,
L=20, T=20, W=40, H=10,
Text='Welcome Screen')
self.insim.send(pyinsim.ISP_BTN,
ReqI=1,
UCID=self.ncn.UCID,
ClickID=2,
BStyle=pyinsim.ISB_LIGHT|pyinsim.ISB_CLICK,
L=20, T=32, W=40, H=10,
Text='Go to options screen')

def unload_buttons(self):
# When screen unloaded delete any buttons sent.
self.insim.send(pyinsim.ISP_BFN,
SubT=pyinsim.BFN_DEL_BTN,
UCID=self.ncn.UCID,
ClickID=1)
self.insim.send(pyinsim.ISP_BFN,
SubT=pyinsim.BFN_DEL_BTN,
UCID=self.ncn.UCID,
ClickID=2)

def button_click(self, btc):
# Handle button click event.
if btc.ClickID == 2:
# Tell the screen manager to transition to the OptionsScreen.
self.screen_manager.transition(OptionsScreen())

# Example options screen, same as WelcomeScreen really.
class OptionsScreen(Screen):
def __init__(self):
Screen.__init__(self)

def load_buttons(self):
self.insim.send(pyinsim.ISP_BTN,
ReqI=1,
UCID=self.ncn.UCID,
ClickID=1,
BStyle=pyinsim.ISB_DARK,
L=20, T=20, W=40, H=10,
Text='Options Screen')
self.insim.send(pyinsim.ISP_BTN,
ReqI=1,
UCID=self.ncn.UCID,
ClickID=2,
BStyle=pyinsim.ISB_LIGHT|pyinsim.ISB_CLICK,
L=20, T=32, W=40, H=10,
Text='Go to welcome')

def unload_buttons(self):
self.insim.send(pyinsim.ISP_BFN,
SubT=pyinsim.BFN_DEL_BTN,
UCID=self.ncn.UCID,
ClickID=1)
self.insim.send(pyinsim.ISP_BFN,
SubT=pyinsim.BFN_DEL_BTN,
UCID=self.ncn.UCID,
ClickID=2)

def button_click(self, btc):
if btc.ClickID == 2:
# Tell ScreenManager for this player to transition
# back to the WelcomeScreen.
self.screen_manager.transition(WelcomeScreen())

class InSimApp(object):
def __init__(self):
# Dict to store the ScreenManager for each player.
self.screen_managers = {}

# Create new InSim connection.
self.insim = pyinsim.insim('127.0.0.2', 29999, Admin='', IName='InSimFSM')

# Bind packet events.
self.insim.bind(pyinsim.ISP_ISM, self.insim_multi)
self.insim.bind(pyinsim.ISP_NCN, self.new_connection)
self.insim.bind(pyinsim.ISP_CNL, self.connection_leave)
self.insim.bind(pyinsim.ISP_BTC, self.button_click)

# Request multiplayer packet on connect.
self.insim.send(pyinsim.ISP_TINY, ReqI=1, SubT=pyinsim.TINY_ISM)

def insim_multi(self, insim, ism):
# When connected to host request connections list.
insim.send(pyinsim.ISP_TINY, ReqI=1, SubT=pyinsim.TINY_NCN)

def new_connection(self, insim, ncn):
# Create new ScreenManager for this connection and add it
# to the screen_managers dictionary.
screen_manager = ScreenManager(insim, ncn)
self.screen_managers[ncn.UCID] = screen_manager

# Tell ScreenManager to display the welcome screen for this player.
screen_manager.transition(WelcomeScreen())

def connection_leave(self, insim, cnl):
# When a connection leaves delete their screen manager.
del self.screen_managers[cnl.UCID]

def button_click(self, insim, btc):
# When packet received pass it on to the connections ScreenManager, which will
# then pass it on to the currently viewed screen.
self.screen_managers[btc.UCID].button_click(btc)


if __name__ == '__main__':
# Create new InSim app.
InSimApp()

# Start all InSim connections and poll for packet events.
pyinsim.run()

You can download the full program (without comments) below.
Last edited by DarkTimes, .
DarkTimes
S2 licensed
Quote from broken :is this the right word? I mean, it's a function in PHP, but is this what it's called generally in programming as well?

It's called a method in C#. As a general rule a function inside a class is called a method, outside of one it's called a function. Functions may also be refereed to as routines. All these terms are pretty interchangeable really.
DarkTimes
S2 licensed
Quote from the_angry_angel :The main benefit that I see over things like PRISM and pyinsim is that it's non-blocking by default, meaning you dont have to worry about yielding, or threads and the sync issues you get.

Just a small correction in that as of version 2.0 pyinsim has been non-blocking and runs everything on a single thread.
DarkTimes
S2 licensed
I had a good idea for an alternative way to handle events which is quite neat.

def version(insim, ver):
print ver.InSimVer

def message_out(insim, mso):
print mso.Msg

insim = pyinsim.insim('127.0.0.1', 29999, ReqI=1, Admin='', IName='Test')

# Bind event handlers
insim.ISP_VER = version
insim.ISP_MSO = message_out

pyinsim.run()

As you see you just create a variable with the packet name and assign it the function you want to call when that packet is received. These member variables don't need to exist, they're resolved dynamically, which turns out to be very simple.

def raise_packet_event(self, data):
ptype = get_packet_type(data[1])
pname = get_packet_name(ptype) # Get packet name

# Get packet handler and check it is a function we can call
packet_event = getattr(self, pname)
if packet_event and hasattr(packet_event, '__call__'):
packet = PACKET_MAP[ptype](data) # Create packet
packet_event(self, packet) # Call event handler

You could also modify it to allow you to have a list of functions to call.

insim.ISP_VER = [version1, version2]

I may well have a go at implementing this in the main library, as I really do like the simplified syntax it allows.
Last edited by DarkTimes, .
DarkTimes
S2 licensed
Yeah, something more LFS related could be useful.

I only brought up the XNA Framework as for C# developers it does contain a lot of useful objects and methods for dealing with 2D and 3D maths. For instance if you wanted to find the distance between two points with XNA it's a matter of doing this...

Vector2 a = new Vector2(164, 476);
Vector2 b = new Vector2(435, 676);
float distance = Vector2.Distance(a, b);

There are also lots of helpers for things like collision detection and a very complete maths library.
Last edited by DarkTimes, .
DarkTimes
S2 licensed
I did a quick and dirty port to C#. It could probably be improved and made more C#-ish, but it'll do I guess.

It should be noted that if you're going to do lots of vector math in C# I'd recommend getting the XNA Framework library, as it has tons of stuff for handling vectors.
Last edited by DarkTimes, .
DarkTimes
S2 licensed
Yeah, you're probably right. I was just thinking you'd need to store any byte, word or unsigned value as a different data-type (short, int and long respectively). I don't know why the makers of Java decided to do it this way, I guess it made sense at the time.
DarkTimes
S2 licensed
You're probably going to have more trouble when you figure out that Java doesn't support unsigned integers. There is a whole open-source Java InSim library that you can steal code from (including OutGauge code), where it would seem that someone else has solved all these problems before you.

http://sourceforge.net/projects/jinsim/
Last edited by DarkTimes, .
DarkTimes
S2 licensed
You could try using track nodes to figure it out. You could keep track of whether the player is going up through the nodes or down, to track what direction they are going. Then to get the car in front you can just find the car that is on the next closest node in the direction they are travelling. This wouldn't work for open-configs, and probably wouldn't be perfect, but it might just work dammit.
Last edited by DarkTimes, .
DarkTimes
S2 licensed
I have been contiuning work on InSim.NET 3.0.0, and it's coming along, although slowly. I posted before about how I wanted to fix the mishmash of naming conventions and also make it generally more conhesive and easier to use. Anyway, I thought I'd give a little preview of how the library is looking at the moment. Here is a typical small example app that prints the lap time of each player to the console whenever they complete a lap.


<?php 
using System
;
using System.Collections.Generic;
using InSimDotNet;
using InSimDotNet.Packets;

namespace 
InSimTest {
    class 
ExampleInSimApp {
        
Dictionary<intNewPlayerplayers = new Dictionary<intNewPlayer>(32);

        
void RunInSim() {
            
// Create new InSimClient object.
            
InSimClient client = new InSimClient();

            
// Hook up packet events.
            
client.InSimMultiPacket += client_InSimMultiPacket;
            
client.NewPlayerPacket += client_NewPlayerPacket;
            
client.PlayerLeavePacket += client_PlayerLeavePacket;
            
client.LapPacket += client_LapPacket;

            
// Initialize InSim.
            
client.Initialize(new InSimSettings {
                
Host "127.0.0.1",
                
Port 29999,
                
Admin String.Empty,
                
Name "Test",
            });

            
// Request ISM packet to be sent.
            
client.Send(new Tiny 
                
RequestId 1
                
SubType TinyType.InSimMulti 
            
});

            
// Stop program from exiting.
            
Console.WriteLine("Press any key to exit...");
            
Console.ReadKey(true);
        }

        
void client_InSimMultiPacket(object senderPacketEventArgs<InSimMultie) {
            
InSimClient client = (InSimClient)sender// Get InSimClient.

            // Request all players to be sent.
            
client.Send(new Tiny 
                
RequestId 1
                
SubType TinyType.NewPlayer
            
});
        }

        
void client_NewPlayerPacket(object senderPacketEventArgs<NewPlayere) {
            
NewPlayer player e.Packet;

            
players[player.PlayerId] = player // Add player to players dictionary.
        
}

        
void client_PlayerLeavePacket(object senderPacketEventArgs<PlayerLeavee) {
            
players.Remove(e.Packet.PlayerId); // Remove player from dictionary.
        
}

        
void client_LapPacket(object senderPacketEventArgs<Lape) {
            
// The EventArgs contains the packet.
            
Lap lap e.Packet;

            
// Get player from players dictionary.
            
NewPlayer player players[lap.PlayerId];

            
// Write lap time to console.
            
Console.WriteLine("Lap {0} ({1}): "player.PlayerNamelap.LapTime);
        }
    }
}
?>

A lot has changed, in no particular order:
  • InSim has been renamed InSimClient
  • Packets are no longer bound, they're just events
  • The names of packets and attributes have been changed to be consistent with .NET naming guidelines
  • The packet attributes have been improved to make it easier to access information
Plus a huge amount more! Anyway as I say just a preview, it might all change.
Last edited by DarkTimes, .
DarkTimes
S2 licensed
Quote from Kid222 :I wish i could find this, before i uninstalled it, UI is really massive pain in arsch.

At the risk of being flamed, I think the Skyrim UI is really good. It's not perfect, but it's incredibly easy and quick to navigate. The trick is to use WASD to navigate, E to confirm and TAB to cancel. At first I found it a little tricky, but once I got used to it I found it one of the easiest game UIs to use. In fact when I play other games I get annoyed because they're not as fluid.
Last edited by DarkTimes, .
DarkTimes
S2 licensed
I fixed a small bug that could cause the MSO msg text to be wrong if the player's name contained non-ASCII characters.

Download V2.0.13
DarkTimes
S2 licensed
Quote from Krayy :Found this that covers most of it: http://weblogs.asp.net/lhunt/attachment/591275.ashx

That list of standards is pretty good.

Personally I'd suggest following the .NET Design Guidelines for Developing Class Libraries and the Microsoft Internal Coding Guidelines
DarkTimes
S2 licensed
Yeah, the OutGauge object is local to that method, so it gets disposed before the method exits. Also the label is being updated from a different thread from the UI, so Windows won't like that. Need to invoke the label text update onto the UI thread.

private OutGauge outgauge = new OutGauge();

private void button1_Click(object sender, EventArgs e)
{
// Attach event handler to packet-received event.
outgauge.PacketReceived += new EventHandler<OutGaugeEventArgs>(outgauge_PacketReceived);
outgauge.TimedOut += new EventHandler(outgauge_TimedOut);

// Start listening for packets from LFS.
outgauge.Connect("127.0.0.1", 30000);
}

static void outgauge_TimedOut(object sender, EventArgs e)
{
MessageBox.Show("OutGauge timed out!");
}

private void outgauge_PacketReceived(object sender, OutGaugeEventArgs e)
{
// Handle packet.
float FValue = e.RPM;
int FValue1 = Convert.ToInt16(FValue);
string final = Convert.ToString(FValue1);
if (FValue > 0)
{
Invoke(new Action(()=> label1.Text = final));
}
}

DarkTimes
S2 licensed
Try this...


<?php 
public partial class Form1 Form
{
    
// Create new InSim object
    
InSim insim = new InSim();

    public 
Form1()
    {
        
InitializeComponent();
    }

    private 
void Form1_Load(object senderEventArgs e)
    {
        
// Initailize InSim
        
insim.Initialize(new InSimSettings
        
{
            
Host "127.0.0.1"// Host where LFS is runing
            
Port 29999// Port to connect to LFS through
            
Admin "xxx"// Optional game admin password
        
});
    }

    private 
void button1_Click(object senderEventArgs e)
    {
        
// Send message to LFS
        
insim.Send("/msg Hello, I am a InSim!");

        
// Send RCM to LFS
        
insim.Send("/rcm Hello PPL");
        
insim.Send("/rcm_all");

    }

    private 
void button2_Click(object senderEventArgs e)
    {
        
//Say's Hello
        
insim.Send("/msg ^7Hia All!");
    }
}
?>

DarkTimes
S2 licensed
Bah, it would be easy to hate Vettel, but he's just so damned likeable. A great driver and very nice boy, he completely deserves the championship. Frankly you can count his career mistakes on one hand, and that says something!
DarkTimes
S2 licensed
The need and/or ability to stay awake for long periods wanes as you get older. I used to stay up for days at a time, but these days after about 1AM I can barely keep my eyes open.
DarkTimes
S2 licensed
"Lewis cannot use his mind" - harsh words from Massa.

In fairness to Hamilton, at least he's won a race in the last three years. Frankly Massa's time in F1 is pretty much over.
DarkTimes
S2 licensed
Good point from Brundle: if the race finishes like this Vettel will not win the championship.
Last edited by DarkTimes, .
DarkTimes
S2 licensed
If I was Ferrari I would drop Massa and put Di Resta in that red car.

**** Schumacher! Safety car for sure!
DarkTimes
S2 licensed
Quote from GreyBull [CHA] :... or maybe we could scrap that "take the start with Q3 tyres" rule? According to the saying(?), you can't force a horse to drink. Schumacher, Di Resta and Sutil probably would've cruised several seconds off the pace, to preserve their tyres, if you forced them to set a lap.

If a driver doesn't complete a lap in a qualifying session they get a +5 grid penalty. That way if you get into Q3 and don't finish a lap you'll start 15th. Seems fair to me.
DarkTimes
S2 licensed
The only thing worse than Hamilton fanboys are Hamilton haters. You're all as bad as each other, as far as I can tell.

I just wish we could have a race where it didn't dissolve into an argument about Hamilton.
DarkTimes
S2 licensed
You remind me of Stewie: http://www.youtube.com/watch?v=shoZNsawvmU*

* As with so many YouTube videos the uploader thought it best to cut the joke off before the actual punchline.
DarkTimes
S2 licensed
Oh for the love of god, can we please not do this Hamilton crap every single race?
DarkTimes
S2 licensed
There should be a mandatory lap in qual, can't be arsed with drivers not bothering to qualify.
FGED GREDG RDFGDR GSFDG