The online racing simulator
Searching in All forums
(929 results)
Tutorial 4: Random lights
Racon
S3 licensed
// [Random lights] ////////////////////////////////////////////////////////////

Now let's look at another way to repeat actions, how to store and retrieve
values, and, as we're getting more complicated now, let's look at some simple
live debugging too.

We're going to make a program that will assign random lights to startlight
objects at a set rate, and then we'll fiddle with it while it runs.

In the previous examples we have not needed to communicate between
functions, but this time we're going to count how many times the lights have
changed. We will need to keep track of the count between multiple calls of the
light-changing function. To do this we'll use the PIE object - an object that
contains InSim information, helper functions, and is always there in the
background.

Important note: To use the PIE object, we must first connect to it. We do
this by assigning it to the $PIE variable. This variable can then be used
throughout the function. You must do this inside each function that needs to
use the PIE object. It's fatal PHP error to forget this step, and PIE can't
protect you from forgetting to do this part, sorry!

In our 'PIE_start' function, once we have a $PIE variable, we can use the
'set' function to store data for later use. We'll store some id numbers to
specify which lights to change, a delay to regulate the speed, and a counter
just for example.

We've got a new function 'PIE_clock', which is called every second with the
unix timestamp as a parameter. We'll connect to the PIE object, retrieve the
delay and then use that to call the 'randomiselights' function every so many
seconds.

In our 'randomiselights' function, we again connect to the PIE object first,
as we'll need our stored data. We'll retrieve our array of light ids and loop
through them - for each light we'll set a random set of bulbs. We'll use the
OCO packet to control our lights, specifying objects by the identifier set in
LFS.

Finally, we'll add one to our counter.

Note, you will need the some startlight objects with the correct identifier
id on your layout, and the layout must be optimised before InSim can control
them. The easiest way to do this is to restart the currect session.

Once we see the lights changing, we can do some live debugging:

We can inspect our counter with a text command ";piepeeku lightchanges". We
can also use this to see our current delay with ";piepeeku lightdelay", and we
can alter it (let's say we'll set it to 7 instead of 4), with ";piepokeu
lightdelay 7".

Note that these commands are limited to admins. In host mode, PIE uses the
server admin status to determine who's allowed to run these commands. In local
mode, you will need to specify your account name in the settings.


randomlights.php:


<?php 

function PIE_settings()
  {
    return array(
'flags'      => 0,
                 
'insimname'  => 'RandomLights');
  }
  
function 
PIE_start()
  {
    
$PIE PIE::connect();
    
    
$PIE->set('lightids',     array(2,3,4));
    
$PIE->set('lightdelay',   4);
    
$PIE->set('lightchanges'0);
  }
  
function 
PIE_clock($t)
  {
    
$PIE PIE::connect();
    
    
$delay $PIE->get('lightdelay');
    
$delay max(1$delay);
    if (!(
$t%$delay))
      
randomiselights();
  }
  
function 
randomiselights()
  {
    
$PIE PIE::connect();
    
    
$lights $PIE->get('lightids');
    foreach(
$lights as $light)
      {
        
$bulbs mt_rand(0,15);
        
PIE_sendpacket(array( 'Type'        => ISP_OCO,
                              
'OCOAction'   => OCO_LIGHTS_SET,
                              
'Index'       => AXO_START_LIGHTS,
                              
'Identifier'  => $light,
                              
'Data'        => $bulbs));
      }    
      
    
$count $PIE->get('lightchanges')+1;
    
$PIE->set('lightchanges'$count);
  }
 
?>

command line:

> pie tutorials\randomlights.php host.cfg





// [Debugging in code] ////////////////////////////////////////////////////////

Now, we'll do some more debugging - you can never have enough ways to find
out what's going on! We'll take our previous lights program and modify the
randomiselights function.

We'll build two strings to tell us what's going on as each light is set, one
for console debugging now and another saved in an array for outputting to chat
later.

We use the 'PIE_debugout' function to optionally output debugging messages
to the PIE console. The first parameter is the level of debugging that should
be required for this message to be output, the second is the message itself.

Alternatively, we can use the 'PIE_debugcheck' function to see if a certain
level of debugging is enabled, in order to deal with the debugging ourselves.
Here we'll send it to chat. We'll use a different debugging level so that we
can toggle these two debugging options independently.

We also have a new helper function 'textbulbs', to more easily see which
bulbs should be set for comparison. (Note, this is correct only for startlight
objects, main lights follow a different pattern.)


randomlightsdebug.php:


<?php 

function PIE_settings()
  {
    return array(
'flags'      => 0,
                 
'insimname'  => 'RandomLights');
  }
  
function 
PIE_start()
  {
    
$PIE PIE::connect();
    
    
$PIE->set('lightids',     array(2,3,4));
    
$PIE->set('lightdelay',   4);
    
$PIE->set('lightchanges'0);
  }
  
function 
PIE_clock($t)
  {
    
$PIE PIE::connect();
    
    
$delay $PIE->get('lightdelay');
    
$delay max(1$delay);
    if (!(
$t%$delay))
      
randomiselights();
  }
  
function 
randomiselights()
  {
    
$PIE PIE::connect();
    
    
$lights $PIE->get('lightids');
    
$debug = array();    
    foreach(
$lights as $light)
      {
        
$bulbs mt_rand(0,15);
        
PIE_sendpacket(array( 'Type'        => ISP_OCO,
                              
'OCOAction'   => OCO_LIGHTS_SET,
                              
'Index'       => AXO_START_LIGHTS,
                              
'Identifier'  => $light,
                              
'Data'        => $bulbs));
                              
        
$debug[] = '#'.$light.': '.textbulbs($bulbs);
        
$msg $t.' lights #'.$light.' set to '.$bulbs.' ('.textbulbs($bulbs).')';
        
PIE_debugout(PIE_DEBUGUSER1$msg);
      }    
      
    
$count $PIE->get('lightchanges')+1;
    
$PIE->set('lightchanges'$count);
      
    if (
PIE_debugcheck(PIE_DEBUGUSER2))
      {
        
$msg 'change '.$count.': '.implode(', '$debug);
        
PIE_say($msg);
      }
  }
  
function 
textbulbs($bulbs)
  {
    
$return '';
    
$return .= ($bulbs 1) ? 'R' '-';
    
$return .= ($bulbs 2) ? 'Y' '-';
    
$return .= ($bulbs 8) ? 'G' '-';
    
    return 
$return;
  }

?>

command line:

> pie tutorials\randomlightsdebug.php host.cfg

Tutorial 3: Race start message
Racon
S3 licensed
// [Race start message] ///////////////////////////////////////////////////////

Now let's try sending packets. We'll use buttons instead of chat to send
messages at the start of each race, using the BTN packet.

To send a packet with PIE, we need only make an associative array with the
required fields defined and pass it to the 'PIE_sendpacket' function. PIE sets
default values for all the fields, so you can leave out anything you don't
need. PIE also handles the 'ReqI' and 'Size' fields, so you can forget those
too.

We'll hook the RST packet and send our button packets from there. As we're
making a host InSim, we'll need to specify a UCID to send the buttons to.
We'll use the use the special value 255 to send them to all connections.

Buttons will persist until cleared by another packet, or the user, or the
InSim is closed. So, we'll have to clean it up ourselves after a delay. We
can't wait with a sleep function because PIE needs to keep doing other things
in the meantime, so we will use a callback.

In this case we'll make a 'killbutton' function to send another packet, BFN.
We'll use the 'PIE_setcallback' function to tell PIE when to call our
function, and what value to pass to it as a parameter. (We'd need to use an
array for passing multiple values, but in this case we only need the button's
ClickID). We'll set a time 6 seconds in the future in this case, and pass the
relevent ClickID in the args parameter.

racestartmessage.php:


<?php 

function PIE_RST($data)
  { 
    
$packet = array('Type'     => ISP_BTN,
                    
'UCID'     => 255,
                    
'ClickID'  => 7,
                    
'BStyle'   => ISB_DARK,
                    
'T'        => 55,
                    
'L'        => 60,
                    
'W'        => 80,
                    
'H'        => 10,
                    
'Text'     => '^3Race clean!');  
    
PIE_sendpacket($packet);
                          
    
$time microtime(true) + 6;
    
PIE_setcallback($time'killbutton'$packet['ClickID']); 
  }
  
function 
killbutton($id)
  {
    
PIE_sendpacket(array( 'Type'      => ISP_BFN,
                          
'SubType'   => BFN_DEL_BTN,
                          
'UCID'      => 255,
                          
'ClickID'   => $id));
  }  

?>

host.cfg: (set your own host/port/adminpass)

flags=0
host=1.2.3.4
port=54321
adminpass=secretpassword

Command line:

> pie tutorials\racestartmessage.php host.cfg

Tutorial 2: Hello player
Racon
S3 licensed
// [Hello player] /////////////////////////////////////////////////////////////

Now, let's make a program to greet every player that connects to the server
that we're on. We'll hook the NCN packet by defining a 'PIE_NCN' function and
use the PName field from the data parameter.

helloplayer.php:

<?php 

function PIE_NCN($data)
  {
    
PIE_say('Hello '.$data['PName']);
  }
  
?>

Command line:

> pie tutorials\helloplayer.php





// [Host hello player] ////////////////////////////////////////////////////////

Notice that this program is speaking as you - this is because the program is
in local mode. If we want a server to greet people, we must use host mode and
connect to the server instead of your client.

To connect to a server, we need the IP address, port number and admin
password of the server (these can be found in your host control panel), we
must add our IP address to the access whitelist (access control tab), and we
must set the correct flag settings (specifically, we must ensure that
ISF_LOCAL flag is not set). We can add these settings to our PIE script in two
ways:

Method 1: We'll add these settings to a config file, and use the original
helloplayer.php file unchanged. Please note that you cannot use constants in a
.cfg file. We will use the value 0 for 'flags'.

helloplayer.php file:


<?php 

function PIE_NCN($data)
  {
    
PIE_say('Hello '.$data['PName']);
  }
  
?>

host.cfg: (set your own host/port/adminpass)

flags=0
host=1.2.3.4
port=54321
adminpass=secretpassword

command line:

> pie tutorials\helloplayer.php host.cfg





Method 2: We'll copy the helloplayer.php file to hosthelloworld.php, and set
the settings we need with the 'PIE_settings' function. Here we can use our
InSim flag constants, if required.

hosthelloplayer.php: (set your own host/port/adminpass)


<?php 

function PIE_settings()
  {
    return array(
'flags'      => 0,
                 
'host'       => '1.2.3.4',
                 
'port'       => 54321,
                 
'adminpass'  => 'secretpassword');
  }

function 
PIE_NCN($data)
  {
    
PIE_say('Hello '.$data['PName']);
  }

?>

command line:

> pie tutorials\hosthelloplayer.php

Tutorial 1: Hello world
Racon
S3 licensed
// [Hello world] //////////////////////////////////////////////////////////////

The simplest program we can write for PIE is a 'hello world' program that
connects to your LFS client (local mode). If you are using the default InSim
port of 29999 and no admin password for your client, you don't need to set any
settings. (The admin password can be found in LFS's cfg.txt file as the 'Game
Admin' setting, or can be set inside LFS in the 'start new host' screen)

We'll define a function called 'PIE_start' (called as soon as the connection
is established), and within it we'll use the 'PIE_say' function (general
purpose 'talk' function) to send 'hello world' to chat.

helloworld.php:

<?php 

function PIE_start()
  {
    
PIE_say('Hello world');
  }

?>

Command line:

> pie tutorials\helloworld.php



Note that this program will continue to run after having said 'hello world'.
We can kill it using Ctrl-C in the console window it is running in, or
clicking the X button in the top right, but these are not ideal. Both may
leave the connection open until it times out, and both will cause any
'PIE_shutdown' function to be skipped.

In order to shut it down automatically, we can use the function 'PIE_kill'.
We'll also speak our message locally this time (other players don't see it),
by using the 'PIE_saylocal' function.

helloworldandquit.php:

<?php 

function PIE_start()
  {
    
PIE_saylocal('Hello local world');    
    
PIE_kill();
  }
  
function 
PIE_shutdown()
  {
    
PIE_say('Goodbye world');
  }

?>

Command line:

> pie tutorials\helloworldandquit.php

Tutorial 0: Introduction
Racon
S3 licensed
Note: You should read InSim.txt for an overview of how InSim works. PIE
handles a lot of things for you, but you need to know the difference between
your PLIDs and your UCIDs, which packets you can send/receive, what
instructions you can send, for a few examples.

Note for advanced programmers: please forgive the following
over-explanations, simplifications, terminology shortcuts and outright lies.
PIE is for all levels of coding ability, right down to the
only-just-beginning - the tutorials have to start there and build up! There
is a quick reference if you want a shortcut to the goodies.

// [Introduction] /////////////////////////////////////////////////////////////

PIE is designed to be give easy access to InSim functionality with the PHP
programming language (ver 7.2, CLI mode). It takes care of connection to InSim
(and optionally OutSim and OutGauge) and maintaining the connection, and calls
a user-defined function on receipt of any packet. It can be used to connect to
an LFS client (local mode) or an LFS server (host mode) and comes with some
basic commands and debugging.

PIE is a commandline executable that reads a PHP file (refered to as a PIE
script) and optionally a text configuration file. PIE then uses the settings
provided, or defaults, to initiate a connection to InSim. Once connected, PIE
will wake from sleep at the specified frequency to run a 'tick'. During this
tick, PIE will process any incoming packets, call any pending callback
functions, and return to sleep. PIE runs at between 5 and 50 ticks per second,
default 20.

PIE scripts consist of PHP functions that are called by PIE as appropriate,
such as at startup, on receipt of a packet, at a pre-specified time, etc.

There are functions available to do common tasks such as sending packets,
speaking to chat, etc., and a PIE object provides constantly-updated InSim
information and a data store.

Functions that are called on receipt of an InSim packet are called with a
parameter which consists of all the packet data, unpacked into an associative
array. Some packets with combined values have additional indexes for the
separated values, and all text fields have been made safe for windows (0 byte
stripped, raw SkinID bytes converted to SkinID text string).

PIE will attempt to run at the specified frequency by sleeping less after
longer tick, but you must still take care about the length of time it takes
your code to run. You have plenty of time to communicate with an online
database, for example, as long as your queries do not take longer to complete
than the length of your tick interval. PIE will not be able to process
packets, call callback functions etc., whilst waiting for script code. In
extreme cases, the InSim connection may timeout or the buffer may overflow.


// [Tutorial Overview] ////////////////////////////////////////////////////////

1) Hello world: (Connecting to local client; Sending messages to chat; Local chat; Shutting down PIE)
2) Hello player: (Accepting packets; Connecting to hosts; PIE cfg files)
3) Race start message: (Sending packets; Sending buttons; Callbacks)
4) Random lights: (PIE stored values; Debugging)
5) No-repair reset: (PIE commands; PIE data)
6) Headsup: (OutGauge; File logging)
7) Object info: (Layout objects; Button clicks; Multiple buttons)
Last edited by Racon, .
Racon
S3 licensed
Last edited by Racon, .
Racon
S3 licensed
Last edited by Racon, .
Racon
S3 licensed
There's an experimental option in the newest test patch (https://www.lfs.net/forum/thread/102117), it would be good for Scawen to hear from as many people who suffer this problem as possible.

Quote :- Options... Misc... HTTP buffer test [EXPERIMENTAL]

Racon
S3 licensed
No, private agreement means they have given you permission - mod reviewers will check with the author/owner.
Racon
S3 licensed
Last edited by Racon, .
Racon
S3 licensed
Last edited by Racon, .
Racon
S3 licensed
I don't know what method Scawen uses for AI, but there was a separate AI-driver project with a huge amount of detail here. I used to follow the driver around as he trained now and then, it was fun to watch his progress Smile
Racon
S3 licensed
Quote from Racer Y :You can't run a whole field of these modded cars in single player. And that makes sense cause you can't just code A.I. cars for every single mod that pops up.

You can do that, unless I'm misunderstanding what you mean? I did all my testing of my mods against a field of 31 various AI cars, mostly mods. LFS just has to do some working out when you try a new combo, then the AI can drive the mods.
Racon
S3 licensed
I've just switched the server to 0.7A for the event, for compatibility with LFS Lazy.

If you need a radar for your best chance at winning an upgrade, you got it Thumbs up
Racon
S3 licensed
Is it a small enough job to add a player's license level to the NCN (or NCI) packet? And if not, could it go on a list?

I had always thought it was in InSim somewhere, but having had a need to use it this week I've found I was confusing it with having seen reference to a host's license level. It's not in PubStat, and I've failed to extract it from LFSWorld (calling the relevant page from externally gives an error). Currently I'm scraping forum pages, but there are issues with that too that cause some misses.

There's only one spare byte in NCN, but maybe it could be 2/3 bits in the flags instead. Plenty of space in NCI, if it's OK to restrict the info to InSims who receive that one.
Racon
S3 licensed
There is a way to use photoshop to paint your skin file directly on to a 3D model, but it's not required. A basic skin is just a square jpeg (1024 or 2048 pixels) which has various parts projected onto the model - there are tutorials in the skins section of the forum.
Racon
S3 licensed
Last edited by Racon, .
Win an LFS upgrade voucher - 24th Feb 20:00 UTC
Racon
S3 licensed
**Friday 24th February** at **20:00 UTC**

We're running an S2 special this week, with our highest S2-licenced driver winning an LFS upgrade voucher, courtesy of Kristoffer Andersen!

Please do tell anyone you know on an S2 licence - let's make it a good fight for them Wink

Tracks highlighted in server **!vote** menu, server is set to S2 friendly now for practice.

Racon
S3 licensed
Last edited by Racon, .
Racon
S3 licensed
I really felt for you Gum guys when I heard about that, what a miserable action to pull a mod out of spite. We've had a couple disappear on us and we didn't notice until the round started and we had to scramble to find a replacement there and then. It doesn't matter to us with our format as much as it does to you, but it's still not much fun to have to do.

I like the idea of locking in the availability of a mod once made public, and/or the last public version if made private. I'm not a fan of the option to make things private anyway (no pros, lots of cons), but that one change would help alleviate this, IMHO the worst problem.
Racon
S3 licensed
Last edited by Racon, .
Racon
S3 licensed
Last edited by Racon, .
Racon
S3 licensed
If it were possible for InSim to send and receive setups, we could not only code up a more automated version of AT's current "send your set for checking" system, but allow a whole raft of other things too. It's not quite as plug-n-play as a server setup option, but it's definitely more flexible. (We could allow ranges of values, we could allow freedom in aspects of setup we don't care about, etc.)

It would probably be less work for Scawen, too.
Racon
S3 licensed
There are two types of PNGs used for modelling in LFS, one is RGB and the other is RGBA (with alpha/transparency). You are supplying an RGB type where the editor expects an RGBA type.

It's explained in this thread: https://www.lfs.net/forum/thread/96373
Racon
S3 licensed
Last edited by Racon, .
FGED GREDG RDFGDR GSFDG