The online racing simulator
ABGap (code dump, unsupported)
I won't be optimising, rewriting or otherwise working on this in the future, but I'll post this here anyway. Perhaps as an example of everything one should not do in a plugin.. Feel free to do whatever with the code soup, like, move to code snippets.

ABGap shows a chat message with the gap delta to the car ahead (when the viewed car crosses a split) and behind (when the car behind the viewed car crosses a split).



---

Notes:
- Tested with PHP 7, PRISM version 0.4.6.4.
- Uses SQLite3, so php_sqlite3.dll needs to be enabled in php.ini.
- Gap is to the car that was ahead/behind at the split, not necessarily to the car that is currently ahead or behind if positions have changed.
- Super hacky, ewww.

/path/to/prism/plugins/ABGap.php
Spoiler - click to reveal

<?php 
class ABGap extends Plugins
{
    const 
URL '';
    const 
NAME 'ABGap';
    const 
AUTHOR 'notanillusion';
    const 
VERSION '0.0.5';
    const 
DESCRIPTION 'Gap delta to car ahead and behind';

    public function 
__construct() {        
        
$this->registerPacket('onLap'ISP_LAP);    // record times, display gaps
        
$this->registerPacket('onMci'ISP_MCI);    // update positions
        
$this->registerPacket('onNpl'ISP_NPL);    // add new plids
        
$this->registerPacket('onPll'ISP_PLL);    // remove unusued plids
        
$this->registerPacket('onRst'ISP_RST);    // update total splits and laps, fresh db table
        
$this->registerPacket('onSpx'ISP_SPX);    // record times, display gaps
        
$this->registerPacket('onSta'ISP_STA);    // update viewplid
            
        
$this->consts = array(
            
'GAP_AHEAD' => -1,
            
'GAP_BEHIND' => 1,
            
            
'SPLIT_LAP' => 255
        
);
        
        
$this->db $this->newDb();
        if (!
$this->db)
            die(
'Failed to create database.'.PHP_EOL);

        
$result $this->createTable($this->db);
        if (!
$result)
            die(
'Failed to create table.'.PHP_EOL);
        
        
$this->playerData = array();    // plid => array(pos, lap), ...
        
$this->viewPlid 0;
        
$this->laps 0;
        
$this->splits 0;
    }
    
    public function 
__destruct() {
        
$this->db->close();
    }
    
    
/// *** PACKET FUNCTIONS START *** ///
    
    
public function onLap(IS_LAP $lap) {
        if (!
count($this->playerData))        // ignore hotlap mode
            
return;
        
        
$pos $this->playerData[$lap->PLID]['pos'];
            
        
$q "INSERT INTO timing (plid, lap, split, pos, time)
            VALUES (
$lap->PLID$lap->LapsDone, ".$this->consts['SPLIT_LAP'].", $pos$lap->ETime)";
            
        
$result $this->db->exec($q);
        
        
// Don't check for gap ahead if we're 1st.
        
if ($lap->PLID === $this->viewPlid && $this->playerData[$this->viewPlid]['pos'] > 1) {
            
$plidAhead $this->getPlidAhead($pos$lap->LapsDone$this->consts['SPLIT_LAP']);
            
            
// Sometimes when cars are really close to one another across a split, the SPX for the car behind in positions
            // can arrive first. Don't display gap when that happens.
            
if ($plidAhead === null) {
                return;
            }
            
$this->sendGap($plidAhead$this->consts['GAP_AHEAD'], $lap->LapsDone$this->consts['SPLIT_LAP']);
        }
        
        
// Don't check for gap behind if we're last.
        
if (!$this->isLastPlaceByPlid($this->viewPlid)) {
            if (
$this->getPlidAhead($pos$lap->LapsDone$this->consts['SPLIT_LAP']) == $this->viewPlid)
                
$this->sendGap($lap->PLID$this->consts['GAP_BEHIND'], $lap->LapsDone$this->consts['SPLIT_LAP']);
        }
    }

    public function 
onMci(IS_MCI $mci) {
        foreach (
$mci->Info as $compcar) {
            if (!
$compcar->Position)
                continue;
            
$this->playerData[$compcar->PLID]['pos'] = $compcar->Position;
            
$this->playerData[$compcar->PLID]['lap'] = $compcar->Lap;
        }
    }
    
    public function 
onNpl(IS_NPL $npl) {
        
$this->playerData[$npl->PLID]['pos'] = $npl->NumP;
        
$this->playerData[$npl->PLID]['lap'] = 1;
    }
    
    public function 
onPll(IS_PLL $pll) {
        unset(
$this->playerData[$pll->PLID]);
    }
    
    public function 
onRst(IS_RST $rst) {
        
$this->laps $rst->RaceLaps;
        
$this->splits $this->getNumSplitsRst($rst);
        
        
$this->playerData = array();
        
        
$result $this->createTable($this->db);
        if (!
$result) {
            echo(
'Failed to create table.'.PHP_EOL);
            return;
        }
}
    
    public function 
onSpx(IS_SPX $spx) {
        if (!
count($this->playerData))
            return;
        
        
$pos $this->playerData[$spx->PLID]['pos'];
        
$lap $this->playerData[$spx->PLID]['lap'];
        
        if (!
$lap$lap++;
        
        
$q "INSERT INTO timing (plid, lap, split, pos, time)
            VALUES (
$spx->PLID$lap$spx->Split{$this->playerData[$spx->PLID]['pos']}$spx->ETime)";
            
        
$result $this->db->exec($q);
        
        if (
$spx->PLID === $this->viewPlid && $this->playerData[$this->viewPlid]['pos'] > 1) {
            
$plidAhead $this->getPlidAhead($pos$lap$spx->Split);
            
            if (
$plidAhead === null) {
                return;
            }
            
$this->sendGap($plidAhead$this->consts['GAP_AHEAD'], $lap$spx->Split);
        }
        
        if (!
$this->isLastPlaceByPlid($this->viewPlid)) {
            if (
$this->getPlidAhead($pos$lap$spx->Split) == $this->viewPlid) {
                
$this->sendGap($spx->PLID$this->consts['GAP_BEHIND'], $lap$spx->Split);
            }
        }
    }
    
    public function 
onSta(IS_STA $sta) {
        
$this->viewPlid $sta->ViewPLID;
    }
    
    
/// *** PACKET FUNCTIONS END *** ///
    
    
function createTable($db) {
        
$q 'DROP TABLE timing';
        @
$this->db->exec($q);
        
        
$q 'CREATE TABLE timing(
            id INTEGER PRIMARY KEY ASC,
            plid INTEGER,
            lap INTEGER,
            split INTEGER,
            pos INTEGER,
            time INTEGER
        )'
;
        
        
$result $this->db->exec($q);

        return 
$result;
    }
    
    function 
getNumSplitsRst($rst) {
        
$splits 0;
        
        for (
$i 3$i 0$i--) {
            if (
$rst->{"Split$i"} && $rst->{"Split$i"} != 65535) {
                
$splits $i;
                break;
            }
        }
        return 
$splits;
    }
    
    function 
getPlidAhead($pos$lap$split) {
        
$pos--;

        
$q "SELECT plid FROM timing WHERE pos=$pos AND lap=$lap AND split=$split;";
        
$result $this->db->query($q);
        
        if (
$result === false)
            return;
        
$row $result->fetchArray();
        
        return 
$row['plid'];
    }
    
    function 
getTime($plid$lap$split) {
        
$q "SELECT * FROM timing WHERE plid=$plid AND lap=$lap AND split=$split;";
        
$result $this->db->query($q);
        
        
$time 0;

        while(
$row $result->fetchArray()) {
            if (
array_key_exists('time'$row))
                
$time $row['time'];
        }
        
        return 
$time;
    }
    
    function 
isLastPlaceByPlid($plid) {
        if (
$this->playerData[$plid]['pos'] >= count($this->playerData)) {
            return 
true;
        }
        
        return 
false;
    }
    
    function 
ms2str ($ms$ahead) {
        
$retval '';
        
        
$hour abs($ms / (1000 60 60));
        if (
$hour >= 1) {
            
$retval .= strval(intval($hour)) . ':';
        }
        
        
$min = ($hour intval($hour)) * 60;
        if (
$hour >= && $min 1) {
            
$retval .= '00:';
        }
        else if (
$min >= 10) {
            
$retval .= strval(intval($min)) . ':';
        }
        else if (
$min && $min 10) {
            
$retval .= '0' strval(intval($min)) . ':';
        }
        
        
$sec = ($min intval($min)) * 60;
        
$retval .= number_format($sec3);
        
        if (
$ms >= && $ahead 0) {
            
$retval "^1+$retval";
        }
        
        if (
$ms && $ahead 0) {
            
$retval "^2-$retval";
        }
        
        if (
$ms >= && $ahead 0) {
            
$retval "^1-$retval";
        }
        
        if (
$ms && $ahead 0) {
            
$retval "^2+$retval";
        }
        
        return 
$retval;
    }
    
    function 
newDb() {
        
$db = new SQLite3(':memory:');
        return 
$db;
    }
    
    function 
getGap($a$b$lap$split) {
        
$plidACurTime $this->getTime($a$lap$split);
        
$plidBCurTime $this->getTime($b$lap$split);
        
$gap $plidACurTime $plidBCurTime;
        
        return 
$gap;
    }
    
    function 
sendGap($plid$mode$lap$split) {
        if (
$split === false)
            return;
        
        
$curGap $this->getGap($this->viewPlid$plid$lap$split);
        
        switch (
$split) {
            case 
255:
                
$split $this->splits;
            break;
            
            case 
1:
                
$split 255;
                
$lap--;
            break;
            
            default:
                
$split--;
            break;
        }
        
        
$prevGap $this->getGap($this->viewPlid$plid$lap$split);
        
        
$gapDelta $curGap $prevGap;

        if (
$mode == $this->consts['GAP_BEHIND']) {
            
$msg '^3Gap delta to car behind: ';
        } else {
            
$msg '^3Gap delta to car ahead: ';
        }
        
        
$timemsg $this->ms2str($gapDelta$mode);
        
$msg .= $timemsg;
        
        
IS_MSL()->Msg($msg)->Send();
    }
}
?>




Or http://pastebin.com/WDFa1VxX
Attached images
lfs-abgap-display.png
I may "borrow" some of this code for the Timing&Scoring plugin.
Added one more ugly hack to sendGap() for dealing with tracks that have zero splits (lap only). Previously it should have thrown index notices or worse. Untested because I can't do so with the AIs, and couldn't find a server running a custom track that has people on it.

Btw, is there a spoiler tag on this forum, because it would be useful for hiding these big blocks of code..

Now it's unsupported Tilt

ABGap v0.0.6
Spoiler - click to reveal

<?php 
class ABGap extends Plugins
{
    const 
URL 'https://www.lfs.net/forum/thread/89031-ABGap-%28code-dump%2C-unsupported%29';
    const 
NAME 'ABGap';
    const 
AUTHOR 'notanillusion';
    const 
VERSION '0.0.6';
    const 
DESCRIPTION 'Gap delta to car ahead and behind';

    public function 
__construct() {        
        
$this->registerPacket('onLap'ISP_LAP);    // record times, display gaps
        
$this->registerPacket('onMci'ISP_MCI);    // update positions
        
$this->registerPacket('onNpl'ISP_NPL);    // add new plids
        
$this->registerPacket('onPll'ISP_PLL);    // remove unusued plids
        
$this->registerPacket('onRst'ISP_RST);    // update total splits and laps, fresh db table
        
$this->registerPacket('onSpx'ISP_SPX);    // record times, display gaps
        
$this->registerPacket('onSta'ISP_STA);    // update viewplid
            
        
$this->consts = array(
            
'GAP_AHEAD' => -1,
            
'GAP_BEHIND' => 1,
            
            
'SPLIT_LAP' => 255
        
);
        
        
$this->db $this->newDb();
        if (!
$this->db)
            die(
'Failed to create database.'.PHP_EOL);

        
$result $this->createTable($this->db);
        if (!
$result)
            die(
'Failed to create table.'.PHP_EOL);
        
        
$this->playerData = array();    // plid => array(pos, lap), ...
        
$this->viewPlid 0;
        
$this->laps 0;
        
$this->splits 0;
    }
    
    public function 
__destruct() {
        
$this->db->close();
    }
    
    
/// *** PACKET FUNCTIONS START *** ///
    
    
public function onLap(IS_LAP $lap) {
        if (!
count($this->playerData))        // ignore hotlap mode
            
return;
        
        
$pos $this->playerData[$lap->PLID]['pos'];
            
        
$q "INSERT INTO timing (plid, lap, split, pos, time)
            VALUES (
$lap->PLID$lap->LapsDone, ".$this->consts['SPLIT_LAP'].", $pos$lap->ETime)";
            
        
$result $this->db->exec($q);
        
        
// Don't check for gap ahead if we're 1st.
        
if ($lap->PLID === $this->viewPlid && $this->playerData[$this->viewPlid]['pos'] > 1) {
            
$plidAhead $this->getPlidAhead($pos$lap->LapsDone$this->consts['SPLIT_LAP']);
            
            
// Sometimes when cars are really close to one another across a split, the SPX for the car behind in positions
            // can arrive first. Don't display gap when that happens.
            
if ($plidAhead === null) {
                return;
            }
            
$this->sendGap($plidAhead$this->consts['GAP_AHEAD'], $lap->LapsDone$this->consts['SPLIT_LAP']);
        }
        
        
// Don't check for gap behind if we're last.
        
if (!$this->isLastPlaceByPlid($this->viewPlid)) {
            if (
$this->getPlidAhead($pos$lap->LapsDone$this->consts['SPLIT_LAP']) == $this->viewPlid)
                
$this->sendGap($lap->PLID$this->consts['GAP_BEHIND'], $lap->LapsDone$this->consts['SPLIT_LAP']);
        }
    }

    public function 
onMci(IS_MCI $mci) {
        foreach (
$mci->Info as $compcar) {
            if (!
$compcar->Position)
                continue;
            
$this->playerData[$compcar->PLID]['pos'] = $compcar->Position;
            
$this->playerData[$compcar->PLID]['lap'] = $compcar->Lap;
        }
    }
    
    public function 
onNpl(IS_NPL $npl) {
        
$this->playerData[$npl->PLID]['pos'] = $npl->NumP;
        
$this->playerData[$npl->PLID]['lap'] = 1;
    }
    
    public function 
onPll(IS_PLL $pll) {
        unset(
$this->playerData[$pll->PLID]);
    }
    
    public function 
onRst(IS_RST $rst) {
        
$this->laps $rst->RaceLaps;
        
$this->splits $this->getNumSplitsRst($rst);
        
        
$this->playerData = array();
        
        
$result $this->createTable($this->db);
        if (!
$result) {
            echo(
'Failed to create table.'.PHP_EOL);
            return;
        }
}
    
    public function 
onSpx(IS_SPX $spx) {
        if (!
count($this->playerData))
            return;
        
        
$pos $this->playerData[$spx->PLID]['pos'];
        
$lap $this->playerData[$spx->PLID]['lap'];
        
        if (!
$lap$lap++;
        
        
$q "INSERT INTO timing (plid, lap, split, pos, time)
            VALUES (
$spx->PLID$lap$spx->Split{$this->playerData[$spx->PLID]['pos']}$spx->ETime)";
            
        
$result $this->db->exec($q);
        
        if (
$spx->PLID === $this->viewPlid && $this->playerData[$this->viewPlid]['pos'] > 1) {
            
$plidAhead $this->getPlidAhead($pos$lap$spx->Split);
            
            if (
$plidAhead === null) {
                return;
            }
            
$this->sendGap($plidAhead$this->consts['GAP_AHEAD'], $lap$spx->Split);
        }
        
        if (!
$this->isLastPlaceByPlid($this->viewPlid)) {
            if (
$this->getPlidAhead($pos$lap$spx->Split) == $this->viewPlid) {
                
$this->sendGap($spx->PLID$this->consts['GAP_BEHIND'], $lap$spx->Split);
            }
        }
    }
    
    public function 
onSta(IS_STA $sta) {
        
$this->viewPlid $sta->ViewPLID;
    }
    
    
/// *** PACKET FUNCTIONS END *** ///
    
    
function createTable($db) {
        
$q 'DROP TABLE timing';
        @
$this->db->exec($q);
        
        
$q 'CREATE TABLE timing(
            id INTEGER PRIMARY KEY ASC,
            plid INTEGER,
            lap INTEGER,
            split INTEGER,
            pos INTEGER,
            time INTEGER
        )'
;
        
        
$result $this->db->exec($q);

        return 
$result;
    }
    
    function 
getNumSplitsRst($rst) {
        
$splits 0;
        
        for (
$i 3$i 0$i--) {
            if (
$rst->{"Split$i"} && $rst->{"Split$i"} != 65535) {
                
$splits $i;
                break;
            }
        }
        return 
$splits;
    }
    
    function 
getPlidAhead($pos$lap$split) {
        
$pos--;

        
$q "SELECT plid FROM timing WHERE pos=$pos AND lap=$lap AND split=$split;";
        
$result $this->db->query($q);
        
        if (
$result === false)
            return;
        
$row $result->fetchArray();
        
        return 
$row['plid'];
    }
    
    function 
getTime($plid$lap$split) {
        
$q "SELECT * FROM timing WHERE plid=$plid AND lap=$lap AND split=$split;";
        
$result $this->db->query($q);
        
        
$time 0;

        while(
$row $result->fetchArray()) {
            if (
array_key_exists('time'$row))
                
$time $row['time'];
        }
        
        return 
$time;
    }
    
    function 
isLastPlaceByPlid($plid) {
        if (
$this->playerData[$plid]['pos'] >= count($this->playerData)) {
            return 
true;
        }
        
        return 
false;
    }
    
    function 
ms2str ($ms$ahead) {
        
$retval '';
        
        
$hour abs($ms / (1000 60 60));
        if (
$hour >= 1) {
            
$retval .= strval(intval($hour)) . ':';
        }
        
        
$min = ($hour intval($hour)) * 60;
        if (
$hour >= && $min 1) {
            
$retval .= '00:';
        }
        else if (
$min >= 10) {
            
$retval .= strval(intval($min)) . ':';
        }
        else if (
$min && $min 10) {
            
$retval .= '0' strval(intval($min)) . ':';
        }
        
        
$sec = ($min intval($min)) * 60;
        
$retval .= number_format($sec3);
        
        if (
$ms >= && $ahead 0) {
            
$retval "^1+$retval";
        }
        
        if (
$ms && $ahead 0) {
            
$retval "^2-$retval";
        }
        
        if (
$ms >= && $ahead 0) {
            
$retval "^1-$retval";
        }
        
        if (
$ms && $ahead 0) {
            
$retval "^2+$retval";
        }
        
        return 
$retval;
    }
    
    function 
newDb() {
        
$db = new SQLite3(':memory:');
        return 
$db;
    }
    
    function 
getGap($a$b$lap$split) {
        
$plidACurTime $this->getTime($a$lap$split);
        
$plidBCurTime $this->getTime($b$lap$split);
        
$gap $plidACurTime $plidBCurTime;
        
        return 
$gap;
    }
    
    function 
sendGap($plid$mode$lap$split) {
        if (
$split === false)
            return;
        
        if (
$this->splits === 0) {
            
$split 255;
        }
        
        
$curGap $this->getGap($this->viewPlid$plid$lap$split);
        
        switch (
$split) {
            case 
255:
                
$split $this->splits;
            break;
            
            case 
1:
                
$split 255;
                
$lap--;
            break;
            
            default:
                if (
$this->splits === 0) {
                    
$lap--;
                } else {
                    
$split--;
                }
            break;
        }
        
        
$prevGap $this->getGap($this->viewPlid$plid$lap$split);
        
        
$gapDelta $curGap $prevGap;

        if (
$mode == $this->consts['GAP_BEHIND']) {
            
$msg '^3Gap delta to car behind: ';
        } else {
            
$msg '^3Gap delta to car ahead: ';
        }
        
        
$timemsg $this->ms2str($gapDelta$mode);
        
$msg .= $timemsg;
        
        
IS_MSL()->Msg($msg)->Send();
    }
}
?>



http://pastebin.com/KtsS07v5
Congrats on being the first "Approved" Plugin.
Btw, what is the procedure to follow if a submission is updated significantly enough that it may warrant re-approval? Post in the approved thread, re-submit, etc.?
Why so much hassle with a sql database while a memory array could do the job perfectly fine?
0.0.7
- tidying, cleanup
- no sql

Spoiler - click to reveal

<?php 

class ABGap extends Plugins
{
    const 
URL 'https://www.lfs.net/forum/thread/89031-ABGap-%28code-dump%2C-unsupported%29';
    const 
NAME 'ABGap';
    const 
AUTHOR 'notanillusion';
    const 
VERSION '0.0.7';
    const 
DESCRIPTION 'Gap delta to car ahead and behind';
    
    const 
GAP_AHEAD = -1;
    const 
GAP_BEHIND 1;
    
    const 
SPLIT_LAP 255;

    public function 
__construct ()
    {        
        
$this->registerPacket('onLap'ISP_LAP);    // record times, display gaps
        
$this->registerPacket('onMci'ISP_MCI);    // update positions
        
$this->registerPacket('onNpl'ISP_NPL);    // add new plids
        
$this->registerPacket('onPll'ISP_PLL);    // remove unusued plids
        
$this->registerPacket('onRst'ISP_RST);    // update total splits and laps, fresh db table
        
$this->registerPacket('onSpx'ISP_SPX);    // record times, display gaps
        
$this->registerPacket('onSta'ISP_STA);    // update viewplid
        
        
$this->timing = array();        // plid, lap, split, pos, time
        
$this->playerData = array();    // plid => array(pos, lap), ...
        
$this->viewPlid 0;
        
$this->laps 0;
        
$this->splits 0;
    }
    
    
/// *** PACKET FUNCTIONS START *** ///
    
    
public function onLap (IS_LAP $lap)
    {
        if (!
count($this->playerData)) { return; }         // ignore hotlap mode
        
        
$pos $this->playerData[$lap->PLID]['pos'];
        
$this->addTime($lap->PLID$lap->LapsDone$this::SPLIT_LAP$pos$lap->ETime);
        
        
// Don't check for gap ahead if we're 1st.
        
if ($lap->PLID === $this->viewPlid && $this->playerData[$this->viewPlid]['pos'] > 1)
        {
            
$plidAhead $this->getPlidAhead($pos$lap->LapsDone$this::SPLIT_LAP);
            
            
// Sometimes when cars are really close to one another across a split, the SPX for the car behind in positions
            // can arrive first. Don't display gap when that happens.
            
if ($plidAhead === null) { return; }

            
$this->sendGap($plidAhead$this::GAP_AHEAD$lap->LapsDone$this::SPLIT_LAP);
        }
        
        
// Don't check for gap behind if we're last.
        
if (!$this->isLastPlaceByPlid($this->viewPlid))
        {
            if (
$this->getPlidAhead($pos$lap->LapsDone$this::SPLIT_LAP) == $this->viewPlid)
            {
                
$this->sendGap($lap->PLID$this::GAP_BEHIND$lap->LapsDone$this::SPLIT_LAP);
            }
        }
    }

    public function 
onMci (IS_MCI $mci)
    {
        foreach (
$mci->Info as $compcar)
        {
            if (!
$compcar->Position) { continue; }
            
            
$this->playerData[$compcar->PLID]['pos'] = $compcar->Position;
            
$this->playerData[$compcar->PLID]['lap'] = $compcar->Lap;
        }
    }
    
    public function 
onNpl (IS_NPL $npl)
    {
        
$this->playerData[$npl->PLID]['pos'] = $npl->NumP;
        
$this->playerData[$npl->PLID]['lap'] = 1;
    }
    
    public function 
onPll (IS_PLL $pll)
    {
        unset(
$this->playerData[$pll->PLID]);
    }
    
    public function 
onRst (IS_RST $rst)
    {
        
$this->laps $rst->RaceLaps;
        
$this->splits $this->getNumSplitsRst($rst);
        
        
$this->playerData = array();
        
$this->timing = array();
    }
    
    public function 
onSpx (IS_SPX $spx)
    {
        if (!
count($this->playerData)) { return; }
        
        
$pos $this->playerData[$spx->PLID]['pos'];
        
$lap $this->playerData[$spx->PLID]['lap'];
        
        if (!
$lap) { $lap++; }
        
        
$this->addTime($spx->PLID$lap$spx->Split$this->playerData[$spx->PLID]['pos'], $spx->ETime);
        
        if (
$spx->PLID === $this->viewPlid && $this->playerData[$this->viewPlid]['pos'] > 1)
        {
            
$plidAhead $this->getPlidAhead($pos$lap$spx->Split);
            
            if (
$plidAhead === null) { return; }
            
            
$this->sendGap($plidAhead$this::GAP_AHEAD$lap$spx->Split);
        }
        
        if (!
$this->isLastPlaceByPlid($this->viewPlid))
        {
            if (
$this->getPlidAhead($pos$lap$spx->Split) == $this->viewPlid)
            {
                
$this->sendGap($spx->PLID$this::GAP_BEHIND$lap$spx->Split);
            }
        }
    }
    
    public function 
onSta (IS_STA $sta)
    {
        
$this->viewPlid $sta->ViewPLID;
    }
    
    
/// *** PACKET FUNCTIONS END *** ///
    
    
function addTime ($plid$lap$split$pos$time)
    {
        
$entry = array
        (
            
'plid' => $plid,
            
'lap' => $lap,
            
'split' => $split,
            
'pos' => $pos,
            
'time' => $time
        
);
        
        
array_push($this->timing$entry);
    }
    
    function 
getGap ($a$b$lap$split)
    {
        
$plidACurTime $this->getTime($a$lap$split);
        
$plidBCurTime $this->getTime($b$lap$split);
        
$gap $plidACurTime $plidBCurTime;
        
        return 
$gap;
    }
    
    function 
getNumSplitsRst ($rst)
    {
        
$splits 0;
        
        for (
$i 3$i 0$i--)
        {
            if (
$rst->{"Split$i"} && $rst->{"Split$i"} != 65535)
            {
                
$splits $i;
                break;
            }
        }
        
        return 
$splits;
    }
    
    function 
getPlidAhead ($pos$lap$split)
    {
        
$pos--;
        
$plid null;
        
        foreach (
$this->timing as $entry)
        {
            if (
$entry['pos'] == $pos && $entry['lap'] == $lap && $entry['split'] == $split) { $plid $entry['plid']; }
        }
        
        return 
$plid;
    }
    
    function 
getTime ($plid$lap$split)
    {
        
$time 0;
        
        foreach (
$this->timing as $entry)
        {
            if (
$entry['plid'] == $plid && $entry['lap'] == $lap && $entry['split'] == $split) { $time $entry['time']; }
        }
        
        return 
$time;
    }
    
    function 
isLastPlaceByPlid ($plid)
    {
        
$last false;
        
        if (
$this->playerData[$plid]['pos'] >= count($this->playerData)) { $last true; }
        
        return 
$last;
    }
    
    function 
ms2str ($ms$ahead)
    {
        
$retval '';
        
$hour abs($ms / (1000 60 60));
        
        if (
$hour >= 1)    { $retval .= strval(intval($hour)) . ':'; }
        
        
$min = ($hour intval($hour)) * 60;
        
        if (
$hour >= && $min 1)    { $retval .= '00:'; }
        else if (
$min >= 10) { $retval .= strval(intval($min)) . ':'; }
        else if (
$min && $min 10) { $retval .= '0' strval(intval($min)) . ':'; }
        
        
$sec = ($min intval($min)) * 60;
        
$retval .= number_format($sec3);
        
        if (
$ms >= && $ahead 0) { $retval "^1+$retval"; }
        else if (
$ms && $ahead 0) { $retval "^2-$retval"; }
        else if (
$ms >= && $ahead 0) { $retval "^1-$retval"; }
        else if (
$ms && $ahead 0) { $retval "^2+$retval"; }
        
        return 
$retval;
    }
    
    function 
sendGap ($plid$mode$lap$split)
    {
        if (
$split === false) { return; }
        
        if (
$this->splits === 0) { $split $this::SPLIT_LAP; }
        
        
$curGap $this->getGap($this->viewPlid$plid$lap$split);
        
        switch (
$split)
        {
            case 
$this::SPLIT_LAP:
                
$split $this->splits;
                break;
            
            case 
1:
                
$split $this::SPLIT_LAP;
                
$lap--;
                break;
            
            default:
                if (
$this->splits === 0) { $lap--; } 
                else { 
$split--; }
                break;
        }
        
        
$prevGap $this->getGap($this->viewPlid$plid$lap$split);
        
        
$gapDelta $curGap $prevGap;

        if (
$mode == $this::GAP_BEHIND) { $msg '^3Gap delta to car behind: '; }
        else { 
$msg '^3Gap delta to car ahead: '; }
        
        
$timemsg $this->ms2str($gapDelta$mode);
        
$msg .= $timemsg;
        
        
IS_MSL()->Msg($msg)->Send();
    }
}

?>

Now thats an excellent job.. Will test it Smile
SplStructs if you want to be really fancy.

FGED GREDG RDFGDR GSFDG