The online racing simulator
#1 - Makao
Best lap
I've started playing with PRISM and I admit this is amazing.

I've made simple plugin which displays best laps of each class in the race. I will add some features like results after race. I haven't tested it with more than one car, but it seems to work.


<?php 
php

/**
 * PRSIM Best lap plugin
 * This plugin allows you to display best lap of each class in the multi-class race.
 * 
 * @author Makao
 * @version 0.1.2
 *
 */
class BestLap extends Plugins {
    const 
URL 'http://www.lfsforum.net/showthread.php?t=76342';
    const 
NAME 'Best Lap';
    const 
AUTHOR 'Makao';
    const 
VERSION '0.1.2';
    const 
DESCRIPTION 'Display best lap times for each class in multi-class races.';

    
/**
     * Best laps by class array
     * @access private
     * @var array
     */
    
private $Laps = array(
        
# Default best lap time (1 hour = 360000 ms), PLID: 0
        # class name => time_plid
        
'GTR' => '360000_0',
        
'GT2' => '360000_0',
        
'GT3' => '360000_0',
        
'GT9' => '360000_0',
        
'GTP' => '360000_0',
        
'NGT' => '360000_0',
        
'BGT' => '360000_0',
        
'LRF' => '360000_0',
        
'LX4' => '360000_0',
        
'TBO' => '360000_0',
        
'STD' => '360000_0'
    
);
    
/**
     * All (top) laps array
     * @access private
     * @var array
     */
    
private $TopLaps = array();
    
/**
     * Racing classes array
     * @access private
     * @var array
     */
    
private $Classes = array(
        
# class name => car name, intake restriction, additional mass
        
'GTR' => 'FZR,0,0;FXR,0,0;XRR,0,0',
        
'GT2' => 'FZR,20,0;FXR,23,0;XRR,24,0',
        
'GT3' => 'FZR,28,0;FXR,32,0;XRR,34,0',
        
'GT9' => 'FZR,21,0;FXR,24,0;XRR,25,0',
        
'GTP' => 'FZR,10,0;FXR,10,10;XRR,10,20',
        
'NGT' => 'UFR,0,0;XFR,0,0',
        
'BGT' => 'UFR,45,0;XFR,43,0',
        
'LRF' => 'LX6,0,0;RAC,0,0;FZ5,0,0',
        
'LX4' => 'LX4,0,0',
        
'TBO' => 'XRT,0,0;FXO,0,0;RB4,0,0',
        
'STD' => 'XFG,0,0;XRG,0,0'
    
);
    
/**
     * Drivers array
     * @access private
     * @var array
     */
    
private $Drivers = array();
    
/**
     * Show Results flag
     * @access private
     * @var boolean
     */
    
private $ShowResults FALSE;

    
/**
     * Constructor
     * @access public
     * @return void
     */
    
public function __construct() {
        
# Commands
        
$this->registerLocalCommand('reinit''reinitCmd''Display app main button.');
        
# Packets
        
$this->registerPacket('onPrismConnect'ISP_VER);
        
$this->registerPacket('compareLaps'ISP_LAP);
        
$this->registerPacket('onRestart'ISP_RST);
        
$this->registerPacket('newDriver'ISP_NPL);
    }
    
    
/**
     * Reinit Command
     * @access public
     * @param string $cmd
     * @param int $ucid 
     * @return void
     */
    
public function reinitCmd($cmd$ucid)
    {
        
$bestLapsBTN = new Button(Button::$TO_LOCAL'bestLapsShow''mainButtons');
        
$bestLapsBTN->L(85)->T(0)->W(15)->H(6);
        
$bestLapsBTN->BStyle ISB_DARK ISB_LEFT 1;
        
$bestLapsBTN->registerOnClick($this'DisplayResults');
        
$bestLapsBTN->Text('BEST LAPS')->send();
    }

    
/**
     * Display welcome message
     * @access public
     * @param IS_VER $VER 
     * @return void
     */
    
public function onPrismConnect(IS_VER $VER) {
        
# Hello World!
        
IS_MSL()->Msg('^3PRISM: Best lap plugin (^1' $this::VERSION '^3) connected.')->Send();
        
# Show 'Best Laps' button
        
$this->reinitCmd('reinit'0);
    }

    
/**
     * Compare current lap with best one
     * @access public
     * @param IS_LAP $LAP
     * @return void
     */
    
public function compareLaps(IS_LAP $LAP) {
        if (
$LAP->LapsDone != 1) { # Do not compare laps after 1st lap
            # Compare top laps
            
if (isset($this->TopLaps[$LAP->PLID])) {
                if (
$LAP->LTime $this->TopLaps[$LAP->PLID]) {
                    
$this->TopLaps[$LAP->PLID] = $LAP->LTime;
                }
            } else {
                
$this->TopLaps[$LAP->PLID] = $LAP->LTime;
            }
            
# Compare class laps
            
$class $this->getClass($LAP->PLID);
            if (
$class != '') {
                list(
$best$PLID) = explode('_'$this->Laps[$class]);
                if (
$LAP->LTime $best) {
                    
$this->Laps[$class] = $LAP->LTime '_' $LAP->PLID;
                    
$lapTime timeToString($LAP->LTime);
                    
$client $this->getClientByPLID($LAP->PLID);
                    
# Local message displaying fastest lap of the class
                    
IS_MSL()->Msg('^7' $class ': ^8Fastest lap: ' $lapTime ' by ' $client->PName)->Send();
                }
            }
            
# Update results if opened
            
if ($this->ShowResults == TRUE) {
                
$this->displayResults();
            }
        }
    }

    
/**
     * On restart delete all stored laps and hide results
     * @access public
     * @param IS_RST $RST 
     * @return void
     */
    
public function onRestart(IS_RST $RST) {
        
$this->hideResults(); # Hide results - no data to display
        
foreach ($this->Laps as $class => $time) {
            
$this->Laps[$class] = '360000_0';
        }
        
$this->TopLaps = array();
        
$this->Drivers = array();
        
# I am not sure about it, my imagination and it works :)
        
$this->sendPacket(IS_TINY()->ReqI(255)->SubT(TINY_NPL));
    }

    
/**
     * Add drivers to the array
     * @access public
     * @param IS_NPL $NPL 
     * @return void
     */
    
public function newDriver(IS_NPL $NPL) {
        
$this->Drivers[$NPL->PLID] = array(
            
'Name' => $NPL->PName,
            
'Car' => $NPL->CName,
            
'Class' => $this->assignClass($NPL->CName$NPL->H_TRes$NPL->H_Mass)
        );
    }

    
/**
     * Display final results
     * @access public
     * @return void
     */
    
public function displayResults() {
        
$this->hideResults(); # Hide results to avoid buttons overlaying
        
$this->ShowResults TRUE;
        
$display = array(
            
'left' => 5,
            
'top' => 39,
            
'width' => 60,
            
'height' => 8
        
);
        
# Title button
        
$titleBTN = new Button(Button::$TO_LOCAL'title''bestLapData');
        
$titleBTN->L($display['left'])->T(27)->W($display['width'])->H($display['height'] + 4);
        
$titleBTN->BStyle ISB_DARK ISB_LEFT 1;
        
$titleBTN->Text('Top laps')->send();
        
# Close button
        
$closeBTN = new Button(Button::$TO_LOCAL'close''bestLapData');
        
$closeBTN->L(61)->T(27)->W(4)->H(8);
        
$closeBTN->BStyle ISB_LEFT 5;
        
$closeBTN->registerOnClick($this'HideResults');
        
$closeBTN->Text('X')->send();
        
# Top 3 Laps
        
asort($this->TopLaps);
        for (
$i 1$i <= 3$i++) {
            
$time = ($i == 1) ? current($this->TopLaps) : next($this->TopLaps);
            if (
$time !== FALSE) {
                
$plid key($this->TopLaps);
                
$client $this->getClientbyPLID($plid);
                
$topBTN = new Button(Button::$TO_LOCAL'top''bestLapData');
                
$topBTN->L($display['left'])->T($display['top'])->W($display['width'])->H($display['height']);
                
$topBTN->BStyle ISB_DARK ISB_LEFT;
                
$topBTN->Text('^7'.$i.' ' $client->PName ' ^7' timeToString($time))->send();
                
$display['top'] += 8;
            }
        }
        
# Best Laps (according to classes)
        
foreach ($this->Laps as $class => $data) {
            list(
$time$plid) = explode('_'$data);
            if (
$time != 360000) {
                
$lapTime timeToString($time);
                
$client $this->getClientByPLID($plid);
                
# Single class result button
                
$topClassBTN = new Button(Button::$TO_LOCAL$class'bestLapData');
                
$topClassBTN->L($display['left'])->T($display['top'])->W($display['width'])->H($display['height']);
                
$topClassBTN->BStyle ISB_DARK ISB_LEFT;
                
$topClassBTN->Text('^7' $class ': ^8' $lapTime ' by ' $client->PName)->send();
                
$display['top'] += 8;
            }
        }
    }

    
/**
     * Hide final results
     * @access public
     * @return void
     */
    
public function hideResults() {
        
ButtonManager::removeButtonsByGroup(Button::$TO_LOCAL'bestLapData');
        
$this->ShowResults FALSE;
    }

    
/**
     * Assign a racing class for driver according to the selected car
     * @access private
     * @param string $cname
     * @param int $intake
     * @param int $mass
     * @return string 
     */
    
private function assignClass($cname$intake$mass) {
        foreach (
$this->Classes as $class => $data) {
            
$row explode(';'$data);
            foreach (
$row as $handicaps) {
                list(
$_car$_intake$_mass) = explode(','$handicaps);
                if (
$_car == $cname && $_intake == $intake && $_mass == $mass) {
                    return 
$class;
                }
            }
        }
    }

    
/**
     * Return vechicle class based on PLID
     * @access private
     * @param int $PLID
     * @return string 
     */
    
private function getClass($PLID) {
        foreach (
$this->Drivers as $id => $data) {
            if (
$id == $PLID) {
                return 
$data['Class'];
            }
        }
    }
}
?>

Quote from Makao :I've started playing with PRISM and I admit this is amazing.

Wow, thank you! I was wondering if anything was going to happen with PRISM, as I've not posted an update in quite a while. Turns out that might of been the best thing for it, now with a stable API people are using it!

About the plugin, you could change your URL to 'http://www.lfsforum.net/showthread.php?t=76342' so that when the web interface is done, it will link to here, this thread so that people can get support for your plugin should they need it.
#3 - Makao
Thanks for the feedback, I think I will have opportunity to write more with PRISM, this is fantastic the only thing missing are docs. It's not easy to learn how it work especially if someone started from current version and I didn't follow this project. Anyway this is going to change right now, because I'm impressed.
I would really like to try PRISM out one day, because I've been into PHP programming a bit, but I don't even know what requirements it needs and how are the basics done. Documentation would be awesome, I have to agree with Makao here.
It's hard to document what is not a finished product. Things change.

Is something like this ok for documentation? I am trying to follow the PHP Manual Syntax as much as possible.
#6 - Makao
Quote from Dygear :It's hard to document what is not a finished product. Things change.

Is something like this ok for documentation? I am trying to follow the PHP Manual Syntax as much as possible.

It's more than ok. IMO syntax is not that important for documentation as examples, that's why CodeIgniter User Guide is my favourite.

I've updated my plugin, now it can display results in game, but I have problem with buttons. I can't display buttons to all. I only managed to show them to local and by UCID, when I try 255, nothing appears. Did I miss something?
Quote from Makao :I've updated my plugin, now it can display results in game, but I have problem with buttons. I can't display buttons to all. I only managed to show them to local and by UCID, when I try 255, nothing appears. Did I miss something?

Make sure the Flags for that host does not include ISF_LOCAL, or only in single player will you be able to use the InSim application, or just one your client when connected to servers.

// The ISF_LOCAL flag is important if your program creates buttons.
// It should be set if your program is not a host control system.
// If set, then buttons are created in the local button area, so
// avoiding conflict with the host buttons and allowing the user
// to switch them with SHIFT+B rather than SHIFT+I.

#8 - Makao
Quote from Dygear :Make sure the Flags for that host does not include ISF_LOCAL, or only in single player will you be able to use the InSim application, or just one your client when connected to servers.

I know that, I wasn't sending any flag. I'll try to get over it later.

I've made another update. This time I faced another issue. I am sending ISF_LOCAL now, because this plugin is going to work on local LFS instance. When I was using Shift + B combination I had few fatalities after calling undefined functions from ButtonManager and PRISM was restarting. I removed comments from ButtonManager code excluding removeButton(), because it was redeclared and now it works, but I can't turn this button on by pressing Shift + B one more time. I am not sure what I did.

And one more thing is there any outgauge simple example? "echo RPM" would be more than enough.
Quote from Makao :And one more thing is there any outgauge simple example? "echo RPM" would be more than enough.

I don't currently have any support for outgauge code, but I'll consider it for the next update.
I've forwarded your request to Victor who is responsible for the packet handling modules with PRISM. If he want's to do it he will, if not, I'll get around to it at some point. But I would not expect it any time soon if I have to do it.
Victor is working on it.

FGED GREDG RDFGDR GSFDG