Difference between revisions of "InSim PHP5-Tutorial"
m (Added warning that Tut. only works with "old" insim) |
m |
||
(13 intermediate revisions by one other user not shown) | |||
Line 1: | Line 1: | ||
+ | {{historical}} | ||
+ | |||
This tutorial will only work if you or your webhoster have PHP version 5.0.0 or higher installed (5.1.x recommended!), because the <tt>stream_socket_*</tt>-functions are not available in PHP 4 and the sockets that can be created in PHP 4 using the <tt>socket_*</tt>-functions can not be run really non-blocking, so you can not prevent errors like timeouts. | This tutorial will only work if you or your webhoster have PHP version 5.0.0 or higher installed (5.1.x recommended!), because the <tt>stream_socket_*</tt>-functions are not available in PHP 4 and the sockets that can be created in PHP 4 using the <tt>socket_*</tt>-functions can not be run really non-blocking, so you can not prevent errors like timeouts. | ||
<div style="color: red; font-weight: bold;"><span style="font-size: 20px;">WARNING: This tutorial will only work with the OLD InSim provided up to patch W!</span><br />A new version of this article is expected to come soon.</div> | <div style="color: red; font-weight: bold;"><span style="font-size: 20px;">WARNING: This tutorial will only work with the OLD InSim provided up to patch W!</span><br />A new version of this article is expected to come soon.</div> | ||
== Datatypes == | == Datatypes == | ||
− | The Data types that are mentioned in your docs\InSim.txt file are C/C++ Datatypes and have to be converted to PHP datatypes when you read packages and back to C/C++ Datatypes when you want to send a package to InSim. The PHP-functions <tt>[http:// | + | The Data types that are mentioned in your docs\InSim.txt file are C/C++ Datatypes and have to be converted to PHP datatypes when you read packages and back to C/C++ Datatypes when you want to send a package to InSim. The PHP-functions <tt>[http://php.net/manual/en/function.pack.php pack()]</tt>, <tt>[http://php.net/manual/en/function.unpack.php unpack()]</tt> and <tt>[http://php.net/manual/en/function.str-pad.php str_pad()]</tt> will help us converting. |
=== char === | === char === | ||
Line 10: | Line 12: | ||
// if a part of a package has the datatype "char", it is simply plain text, | // if a part of a package has the datatype "char", it is simply plain text, | ||
// but str_pad() MUST be used to make this part exactly as long as it is told in InSim.txt. | // but str_pad() MUST be used to make this part exactly as long as it is told in InSim.txt. | ||
− | |||
− | |||
− | |||
− | |||
// char-data must not be longer than it is specified in InSim.txt. If someone would specify | // char-data must not be longer than it is specified in InSim.txt. If someone would specify | ||
// an admin-password that is 17 chars long or longer, f.e. $adminpw = "myverylongadminpw"; | // an admin-password that is 17 chars long or longer, f.e. $adminpw = "myverylongadminpw"; | ||
− | // we would have to cut it down to | + | // we would have to cut it down to 15 chars BEFORE str_padding it to 16 (because the 16th place must be the NUL). |
// (logging in to the LfS-Servers InSim-port would work flawlessly because the server | // (logging in to the LfS-Servers InSim-port would work flawlessly because the server | ||
// also only allows the admin-pw to be 16 chars long.) | // also only allows the admin-pw to be 16 chars long.) | ||
− | if (strlen($adminpw) > 16) { | + | if (strlen($adminpw) >= 16) { |
− | $adminpw = substr($adminpw, 0, | + | $adminpw = substr($adminpw, 0, 15); |
} | } | ||
+ | |||
+ | // Example: InSim.txt says the part "Admin" of the InSimInit packet has the datatype char[16], | ||
+ | // but the admin-password of your server is only 8 characters long. So we have to str_pad it: | ||
+ | $adminpw = str_pad($adminpw, 16, "\0"); | ||
+ | |||
+ | |||
+ | // $adminpw is now ready to be sent in the packet. | ||
+ | |||
?> | ?> | ||
</pre> | </pre> | ||
Line 42: | Line 48: | ||
<?php | <?php | ||
// Parts of Packages that have the datatype "word" are strings/ints in PHP and are converted | // Parts of Packages that have the datatype "word" are strings/ints in PHP and are converted | ||
+ | // The pack type 'v' is more appropriate as it's an unsigned short (16bit) that's in the little endian byte order (like the network order). | ||
// using pack() and unpack(): | // using pack() and unpack(): | ||
− | $word_packed = pack(" | + | $word_packed = pack("v", $word); |
// "words" that work properly with unpack must be atleast 2 chars long, so pad it with "\0" | // "words" that work properly with unpack must be atleast 2 chars long, so pad it with "\0" | ||
Line 49: | Line 56: | ||
$word_packed = str_pad($word_packed, 2, "\0"); | $word_packed = str_pad($word_packed, 2, "\0"); | ||
} | } | ||
− | $temp = unpack(" | + | $temp = unpack("v",$word_packed); |
$word_unpacked = $temp[1]; | $word_unpacked = $temp[1]; | ||
?> | ?> | ||
Line 68: | Line 75: | ||
=== unsigned === | === unsigned === | ||
− | + | <pre> | |
+ | <?php | ||
+ | // Parts of Packages that have the datatype "unsigned" are int-variables in PHP and are converted | ||
+ | // using pack() and unpack(): | ||
+ | $unsigned_packed = pack("I", intval($int)); //intval used to ensure datatype is int | ||
+ | |||
+ | $temp = unpack("I",$int_packed); | ||
+ | $unsigned_unpacked = $temp[1]; | ||
+ | ?> | ||
+ | </pre> | ||
=== int === | === int === | ||
Line 96: | Line 112: | ||
=== MSHT === | === MSHT === | ||
+ | <span style="color: red;"><span style="font-weight: bold;">Deprecated</span> <span style="font-style: italic;">It should be noted that this datatype is no longer used by InSim.</span></span> | ||
+ | |||
MSHT is a time-information (Minutes Seconds Hundreths Thousandths) and simply consists of 4 byte-type parts. | MSHT is a time-information (Minutes Seconds Hundreths Thousandths) and simply consists of 4 byte-type parts. | ||
+ | |||
+ | <pre> | ||
+ | <?php | ||
+ | // Parts of Packages that have the datatype struct of 4"byte" that are int-variables in PHP and are converted | ||
+ | // using pack() and unpack(): | ||
+ | $msht_packed = pack("vvvv", $msht); | ||
+ | |||
+ | $msht = unpack("vM\vS\vH\vT",$msht_packed); | ||
+ | $msht_minutes = $temp['M']; | ||
+ | $msht_seconds = $temp['S']; | ||
+ | $msht_hundreths = $temp['H']; | ||
+ | $msht_thousandths = $temp['T']; | ||
+ | ?> | ||
+ | </pre> | ||
== How to build/parse packets == | == How to build/parse packets == | ||
Line 361: | Line 393: | ||
InSimClose: waiting for connection | InSimClose: waiting for connection | ||
</pre> | </pre> | ||
+ | |||
+ | [[Category:InSim]] |
Latest revision as of 00:59, 2 December 2023
This page or section contains outdated information, but is kept for historical reasons. |
This tutorial will only work if you or your webhoster have PHP version 5.0.0 or higher installed (5.1.x recommended!), because the stream_socket_*-functions are not available in PHP 4 and the sockets that can be created in PHP 4 using the socket_*-functions can not be run really non-blocking, so you can not prevent errors like timeouts.
A new version of this article is expected to come soon.
Datatypes
The Data types that are mentioned in your docs\InSim.txt file are C/C++ Datatypes and have to be converted to PHP datatypes when you read packages and back to C/C++ Datatypes when you want to send a package to InSim. The PHP-functions pack(), unpack() and str_pad() will help us converting.
char
<?php // if a part of a package has the datatype "char", it is simply plain text, // but str_pad() MUST be used to make this part exactly as long as it is told in InSim.txt. // char-data must not be longer than it is specified in InSim.txt. If someone would specify // an admin-password that is 17 chars long or longer, f.e. $adminpw = "myverylongadminpw"; // we would have to cut it down to 15 chars BEFORE str_padding it to 16 (because the 16th place must be the NUL). // (logging in to the LfS-Servers InSim-port would work flawlessly because the server // also only allows the admin-pw to be 16 chars long.) if (strlen($adminpw) >= 16) { $adminpw = substr($adminpw, 0, 15); } // Example: InSim.txt says the part "Admin" of the InSimInit packet has the datatype char[16], // but the admin-password of your server is only 8 characters long. So we have to str_pad it: $adminpw = str_pad($adminpw, 16, "\0"); // $adminpw is now ready to be sent in the packet. ?>
byte
<?php // Parts of Packages that have the datatype "byte" are int-variables in PHP and are converted // using pack() and unpack(): $byte_packed = pack("c", $byte); $temp = unpack("c",$byte_packed); $byte_unpacked = $temp[1]; ?>
word
<?php // Parts of Packages that have the datatype "word" are strings/ints in PHP and are converted // The pack type 'v' is more appropriate as it's an unsigned short (16bit) that's in the little endian byte order (like the network order). // using pack() and unpack(): $word_packed = pack("v", $word); // "words" that work properly with unpack must be atleast 2 chars long, so pad it with "\0" if(strlen($word_packed) < 2) { $word_packed = str_pad($word_packed, 2, "\0"); } $temp = unpack("v",$word_packed); $word_unpacked = $temp[1]; ?>
short
<?php // Parts of Packages that have the datatype "short" are int-variables in PHP and are converted // using pack() and unpack(): $short_packed = pack("s", $short); $temp = unpack("s",$short_packed); $short_unpacked = $temp[1]; ?>
unsigned
<?php // Parts of Packages that have the datatype "unsigned" are int-variables in PHP and are converted // using pack() and unpack(): $unsigned_packed = pack("I", intval($int)); //intval used to ensure datatype is int $temp = unpack("I",$int_packed); $unsigned_unpacked = $temp[1]; ?>
int
<?php // Parts of Packages that have the datatype "int" are int-variables in PHP and are converted // using pack() and unpack(): $int_packed = pack("i", intval($int)); //intval used to ensure datatype is int $temp = unpack("i",$int_packed); $int_unpacked = $temp[1]; ?>
float
<?php // Parts of Packages that have the datatype "byte" are float-variables in PHP and are converted // using pack() and unpack(): $float_packed = pack("f", $float); $temp = unpack("f",$float_packed); $float_unpacked = $temp[1]; ?>
MSHT
Deprecated It should be noted that this datatype is no longer used by InSim.
MSHT is a time-information (Minutes Seconds Hundreths Thousandths) and simply consists of 4 byte-type parts.
<?php // Parts of Packages that have the datatype struct of 4"byte" that are int-variables in PHP and are converted // using pack() and unpack(): $msht_packed = pack("vvvv", $msht); $msht = unpack("vM\vS\vH\vT",$msht_packed); $msht_minutes = $temp['M']; $msht_seconds = $temp['S']; $msht_hundreths = $temp['H']; $msht_thousandths = $temp['T']; ?>
How to build/parse packets
Assembling packets
To assemble a packet, simply put the single pieces together using the string concatenation operator. We will use the ISI (InSimInit) packet as example here.
<?php $adminpw = "adminpwd"; $packet = ""; $packet .= "ISI\0"; // Packet-ID $packet .= pack("S", 30000); // 30000 as example response port $packet .= pack("c", 2+4+32); // Connection Flags - see InSim.txt $packet .= pack("c", 1); // NodeSecs - time between packages if (strlen($adminpw) > 16) { $adminpw = substr($adminpw, 0, 16); // Cut down adminpw if too long } $packet .= str_pad($adminpw, 16, "\0"); // Admin-Password if set in LFS host options ?>
Now you could send the packet to InSim.
Disassembling packets
To disassemble a packet, you can simply use substr() and then unpack the data. We will use a simple VER (InSimVersion)-packet as example here.
<?php $packet = receiveVER(); // The function does not exist, just used to make the ex. more readable $lfsVersion = trim(substr($packet, 4, 8)); // char $lfsProduct = trim(substr($packet, 12, 6)); // char $isv_raw = substr($packet, 18, 2); // word if(strlen($isv_raw) < 2) { $isv_raw = str_pad($isv_raw, 2, "\0"); } $temp = unpack("S",$isv_raw); $insimVersion = $temp[1]; ?>
Or you could do it in one fowl swoop, and out put an array.
<?php $packet = unpack('a4VER\A8Version\A6Product\SInSimVer', receiveVER()); print_r($packet); ?>
This could output ...
Array ( 'VER' => 'VER ', 'Version' => '0.5V', 'Product' => 'DEMO', 'InSimVer' => '1 ' )
Connecting to InSim
To connect to InSim, we need to open 2 socket-streams: one that sends data to InSim, and one that receives the answers from InSim. We will use fsockopen() for the sender connection and stream_socket_server() for the receiver.
<?php // CONFIG START $insimIP = '127.0.0.1'; // Your InSim-IP Here $insimPort = 29999; // Your InSim-Port $adminPW = 'adminpwd'; // Your Admin-Password // CONFIG END // create sender filestream $errno = 0; $errstr = ""; $sender = @fsockopen("udp://$insimIP", $insimPort, &$errno, &$errstr, 3); if (!$sender) { die("Error:\nCould not connect to $insimIP:$insimPort\n" . "Error Number: $errno\nError Description: $errstr"); } // create receiver filestream $localport = 30000; $receiver = false; while ($localport <= 65535) { $receiver = @stream_socket_server("udp://0.0.0.0:$localport", $errno, $errstr, STREAM_SERVER_BIND); if (is_resource($receiver)) { break; } $localport++; $receiver = false; } if ($receiver === false) { die("Error:\nCould not bind to $localport\nError Number: $errno\nError Description: $errstr"); } // Make the receiver stream nonblocking to be able to apply timeouts stream_set_blocking($receiver, 0); /* VARIABLES AFTER HERE: $sender : sender filestream $receiver : receiver filestream $localport : port of receiver filestream + config variables */ // We will now have to send an ISI (InSimInit)-packet to InSim to make it accept our requests. // Prepare packet $packet = ""; $packet .= "ISI\0"; // Packet-ID $packet .= pack("S", $localport); // response port $packet .= pack("c", 2+4+32); // Connection Flags - see InSim.txt $packet .= pack("c", 1); // NodeSecs - time between packages if (strlen($adminPW) > 16) { $adminPW = substr($adminPW, 0, 16); // Cut down adminpw if too long } $packet .= str_pad($adminPW, 16, "\0"); // Admin-Password if set in LFS host options // Send packet fwrite($sender, $packet, strlen($packet)); // Third parameter to make PHP ignore magic_quotes-setting ?>
If everything works, LfS should display a message like "InSimInit: Port 30000".
Disconnecting from InSim
Disconnecting from InSim is a lot easier. We have to send an InSimPack with "ISC" as ID to tell InSim we won't send any more packages and then we can close the sender and receiver filestreams.
<?php $packet = "ISC\0" . pack("i", intval(0)); fwrite($sender, $packet, strlen($packet)); fclose($sender); fclose($receiver); ?>
LfS will display "InSimClose: Waiting for connection".
Easy example
In this example, we will connect to InSim, receive Version-Informations of LFS and the LFS hostname, and then we will send a text message to the server and force it to reinitialize. I will assume that you have copied the script-piece from "Connecting to InSim" to a file called "connect.inc.php" (and of course updated the configuration part so it fits to your LfS-server), and that you have copied the script-piece from "Disconnecting from InSim" to a file called "disconnect.inc.php". This will make the example more readable and will prevent too much useless (because doubled) code here.
<?php // Output will be plain text header("Content-Type: text/plain; Charset=ISO-8859-1"); // Connect to InSim require "connect.inc.php"; echo "Connected! Requesting Version packet...\n\n"; // The version-request packet is a simple InSimPack with ID "VER" and Value 0. $packet = "VER\0" . pack("i", intval(0)); // send packet fwrite($sender, $packet, strlen($packet)); $packet = false; // receive answer from LfS: a packet with ID "VER" $timeout = time() + 2; while (!$packet && time() <= $timeout) { if ($packet = fread($receiver, 256)) { break; } } // check if really a version-packet arrived or something else we cant deal with at the moment if (!$packet || substr($packet, 0, 3) != "VER") { echo "No version package arrived, sorry :-(\n\n"; } else { // Parse version-package $lfsV = trim(substr($packet, 4, 8)); // char $lfsP = trim(substr($packet, 12, 6)); // char $isv_raw = substr($packet, 18, 2); // word if(strlen($isv_raw) < 2) { $isv_raw = str_pad($isv_raw, 2, "\0"); } $temp = unpack("S",$isv_raw); $insimV = $temp[1]; // Show some version-information echo "LfS Version-information:\n LfS Product: $lfsP\n" . " LfS Version: $lfsV\n InSim Version: $insimV\n\n"; } echo "Requesting InSimMulti-Package with Hostname now...\n\n"; // Now we request an InSimMulti-Package to get the LfS Hostname (if LfS is in multiplayer mode) // To perform the request, we simply send an InSimPack with ID = "ISM" and Value = 0. $packet = "ISM\0" . pack("i", intval(0)); // send packet fwrite($sender, $packet, strlen($packet)); $packet = false; // receive answer from LfS: a packet with ID "ISM" $timeout = time() + 2; while (!$packet && time() <= $timeout) { if ($packet = fread($receiver, 256)) { break; } } // check if really a ISM-packet arrived or something else we cant deal with at the moment if (!$packet || substr($packet, 0, 3) != "ISM") { echo "No InSimMulti-package arrived, sorry :-(\n\n"; } // Get LfS connection type: are we connected to a client (0) or a server (1)? $type_raw = substr($packet, 4, 1); $temp = unpack("c", $type_raw); $type = $temp[1]; echo "LfS type: " . (($type == 0)?"Client":"Server") . "\n\n"; // Get LfS Hostname $hostname = trim(substr($packet, 8, 32)); if(empty($hostname)) { echo "Not in multiplayer mode\n\n"; } else { echo "Hostname: $hostname"; } // if we are connected to a server, restart it if($type == 1) { echo "Now forcing server restart...\n\n"; // To inform the users, we will first send a MST(MessageTypePack)-packet to the server containing // the information that the server will be restarted now. // then we will force a server reinit, also using a MST. $packet = "MST\0" . str_pad("^1SORRY, BUT THE SERVER WILL BE RESTARTED IN 3 SECONDS", 64, "\0"); fwrite($sender, $packet, strlen($packet)); $packet = "MST\0" . str_pad("/reinit", 64, "\0"); fwrite($sender, $packet, strlen($packet)); } // Disconnect from InSim require "disconnect.inc.php"; ?>
LfS should display the following (if we were connected to a host):
InSimInit: port 30000 host: SORRY, BUT THE SERVER WILL BE RESTARTED IN 3 SECONDS /reinit InSimClose: waiting for connection Host will restart in 3 seconds Track loaded
If we were connected to a client, LfS should display:
InSimInit: port 30000 InSimClose: waiting for connection