#define PACKET_BUFFER_SIZE 512
#define PACKET_MAX_SIZE 512
#define IS_TIMEOUT 5
// Definition for our buffer datatype
struct buffer
{
char buffer[PACKET_BUFFER_SIZE]; // Packet buffer - 512 should be more than enough
unsigned int bytes; // Number of bytes currently in buffer
};
/**
* CInsim class to manage the Insim connection and processing of the packets
*/
class CInsim
{
private:
SOCKET sock; // TCP Socket (most packets)
SOCKET sockudp; // UDP Socket (if requested, for NLP and MCI)
byte using_udp; // 1 if we are using UDP for NLP or MCI packets
struct buffer gbuf; // Our global buffer
struct buffer lbuf; // Our local buffer
char packet[PACKET_MAX_SIZE]; // A buffer where the current packet is stored
fd_set readfd, exceptfd; // File descriptor watches
struct timeval select_timeout; // timeval struct for the select() call
struct buffer udp_gbuf; // (for NLP and MCI packets via UDP) Our global buffer
struct buffer udp_lbuf; // (for NLP and MCI packets via UDP) Our local buffer
char udp_packet[PACKET_MAX_SIZE]; // (for NLP and MCI packets via UDP) A buffer where the current packet is stored
fd_set udp_readfd, udp_exceptfd; // (for NLP and MCI packets via UDP) File descriptor watches
struct timeval udp_select_timeout; // (for NLP and MCI packets via UDP) timeval struct for the select() call
ofstream logfile;
public:
CInsim::CInsim(); // Constructor
// "int init(...)" Establishes connection with the socket and insim.
//+ The last argument ch_ver is a pointer to a IS_VER struct. If it's used an IS_VER packet
//+ will be returned. If ch_ver is not used in the call no IS_VER will be requested/returned.
int init (char *addr, word port, char *product, char *admin, struct IS_VER *pack_ver = NULL, byte prefix = 0, word flags = 0, word interval = 0, word udpport = 0);
int isclose(); // Closes connection from insim and from the socket
int next_packet(); // Gets next packet ready into "char packet[]"
char peek_packet(); // Returns the type of the current packet
void* get_packet(); // Returns a pointer to the current packet. Must be casted
int send_packet(void* packet); // Sends a packet to the host
int udp_next_packet(); // (UDP) Gets next packet ready into "char udp_packet[]"
char udp_peek_packet(); // (UDP) Returns the type of the current packet
void* udp_get_packet(); // (UDP) Returns a pointer to the current packet. Must be casted
int udp_send_packet(void* packet); // (UDP) Sends a packet to the host
};
/**
* Initialize the socket and the Insim connection
* If "struct IS_VER *pack_ver" is set it will contain an IS_VER packet after returning. It's an optional argument
*/
int CInsim::init (char *addr, word port, char *product, char *admin, struct IS_VER *pack_ver, byte prefix, word flags, word interval, word udpport)
{
// Initialise WinSock
// Only required on Windows
WSADATA wsadata;
if (WSAStartup(0x202, &wsadata) == SOCKET_ERROR)
{
WSACleanup();
return -1;
}
// Create the TCP socket - this defines the type of socket
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Could we get the socket handle? If not the OS might be too busy or have run out of available socket descriptors
if (sock == INVALID_SOCKET)
{
closesocket(sock);
WSACleanup();
return -1;
}
// Resolve the IP address
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
struct hostent *hp;
hp = gethostbyname(addr);
if (hp != NULL)
saddr.sin_addr.s_addr = *((unsigned long*)hp->h_addr);
else
saddr.sin_addr.s_addr = inet_addr(addr);
// Set the port number in the socket structure - we convert it from host byte order, to network
saddr.sin_port = htons(port);
// Now the socket address structure is full, lets try to connect
if (connect(sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0)
{
closesocket(sock);
WSACleanup();
return -1;
}
// If the user asked for NLP or MCI packets and defined an udpport
if ((udpport) && (flags >= ISF_NLP))
{
// Create the UDP socket - this defines the type of socket
sockudp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
// Could we get the socket handle? If not the OS might be too busy or have run out of available socket descriptors
if (sockudp == INVALID_SOCKET)
{
closesocket(sock);
closesocket(sockudp);
WSACleanup();
return -1;
}
// Resolve the IP address
struct sockaddr_in udp_saddr;
memset(&udp_saddr, 0, sizeof(udp_saddr));
udp_saddr.sin_family = AF_INET;
struct hostent *udp_hp;
udp_hp = gethostbyname(addr);
if (udp_hp != NULL)
udp_saddr.sin_addr.s_addr = *((unsigned long*)udp_hp->h_addr);
else
udp_saddr.sin_addr.s_addr = inet_addr(addr);
// Set the UDP port number in the UDP socket structure - we convert it from host byte order, to network
udp_saddr.sin_port = htons(port);
// Connect the UDP using the same address as in the TCP socket
if (connect(sockudp, (struct sockaddr *) &udp_saddr, sizeof(udp_saddr)) < 0)
{
closesocket(sock);
closesocket(sockudp);
WSACleanup();
return -1;
}
// We are using UDP!
using_udp = 1;
}
// Ok, so we're connected. First we need to let LFS know we're here by sending the IS_ISI packet
struct IS_ISI isi_p;
memset(&isi_p, 0, sizeof(struct IS_ISI));
isi_p.Size = sizeof(struct IS_ISI);
isi_p.Type = ISP_ISI;
if (pack_ver != NULL) // We request an ISP_VER if the caller asks for it
isi_p.ReqI = 1;
isi_p.Prefix = prefix;
isi_p.UDPPort = udpport;
isi_p.Flags = flags;
isi_p.Interval = interval;
memcpy(isi_p.IName, product, sizeof(isi_p.IName)-1);
memcpy(isi_p.Admin, admin, 16);
// Send the initialization packet
if(send_packet(&isi_p) < 0)
{
if (using_udp)
closesocket(sockudp);
closesocket(sock);
WSACleanup();
return -1;
}
// If an IS_VER packet was requested
if (pack_ver != NULL)
{
if (next_packet() < 0) // Get next packet, supposed to be an IS_VER
{
if (isclose() < 0)
{
if (using_udp)
closesocket(sockudp);
closesocket(sock);
WSACleanup();
return -1;
}
return -1;
}
switch (peek_packet()) // Check if the packet returned was an IS_VER
{
case ISP_VER: // It was, get it!
memcpy(pack_ver, (struct IS_VER*)get_packet(), sizeof(struct IS_VER));
break;
default: // It wasn't, something went wrong. Quit
if (isclose() < 0)
{
if (using_udp)
closesocket(sockudp);
closesocket(sock);
WSACleanup();
}
return -1;
}
}
return 0;
}
/**
* Get next packet ready
* This function also keeps the connection alive as long as you keep calling it
*/
int CInsim::next_packet()
{
unsigned char oldp_size, p_size;
bool alive = true;
while (alive) // Keep the connection alive!
{
alive = false;
oldp_size = (unsigned char)*lbuf.buffer;
if ((lbuf.bytes > 0) && (lbuf.bytes >= oldp_size)) // There's an old packet in the local buffer, skip it
{
// Copy the leftovers from local buffer to global buffer
memcpy(gbuf.buffer, lbuf.buffer+oldp_size, lbuf.bytes-oldp_size);
gbuf.bytes = lbuf.bytes - oldp_size;
// Copy from global buffer back to the beginning of local buffer
memset(lbuf.buffer, 0, PACKET_BUFFER_SIZE);
memcpy(lbuf.buffer, gbuf.buffer, gbuf.bytes);
lbuf.bytes = gbuf.bytes;
}
p_size = (unsigned char)*lbuf.buffer;
while ((lbuf.bytes < p_size) || (lbuf.bytes < 1)) // Read until we have a full packet
{
// Set the timeout period
select_timeout.tv_sec = IS_TIMEOUT;
select_timeout.tv_usec = 0;
// Clear them
FD_ZERO(&readfd);
FD_ZERO(&exceptfd);
// Set them to watch our socket for data to read and exceptions that maybe thrown
FD_SET(sock, &readfd);
FD_SET(sock, &exceptfd);
int rc = select(sock + 1, &readfd, NULL, &exceptfd, &select_timeout);
if (rc == 0) // Timeout
continue;
if (rc < 0) // An error occured
return -1;
if (FD_ISSET(sock, &readfd)) // We got data!
{
// Recieve any waiting bytes
int retval = recv(sock, lbuf.buffer + lbuf.bytes, PACKET_BUFFER_SIZE - lbuf.bytes, 0);
// Deal with the results
if (retval == 0) // Connection has been closed at the other end
return -2;
if (retval < 0) // An error ocurred
return -1;
p_size = *lbuf.buffer;
lbuf.bytes += retval;
}
else if (FD_ISSET(sock, &exceptfd)) // An exception occured - we want to quit
return -1;
}
memset(packet, 0, PACKET_MAX_SIZE);
memcpy(packet, lbuf.buffer, p_size);
logfile << "UDP, ISP_" << (int)peek_packet() << ", " << (float)clock()/CLOCKS_PER_SEC << ", recv" << endl;
if ((peek_packet() == ISP_TINY) && (*(packet+3) == TINY_NONE))
{
alive = true;
struct IS_TINY keepalive;
keepalive.Size = sizeof(struct IS_TINY);
keepalive.Type = ISP_TINY;
keepalive.ReqI = 0;
keepalive.SubT = TINY_NONE;
// Send it back
if (send_packet(&keepalive) < 0)
return -1;
}
}
return 0;
}
/**
* Get next UDP packet ready
*/
int CInsim::udp_next_packet()
{
unsigned char oldp_size, p_size;
oldp_size = (unsigned char)*udp_lbuf.buffer;
if ((udp_lbuf.bytes > 0) && (udp_lbuf.bytes >= oldp_size)) // There's an old packet in the local buffer, skip it
{
// Copy the leftovers from local buffer to global buffer
memcpy(udp_gbuf.buffer, udp_lbuf.buffer+oldp_size, udp_lbuf.bytes-oldp_size);
udp_gbuf.bytes = udp_lbuf.bytes - oldp_size;
// Copy from global buffer back to the beginning of local buffer
memset(udp_lbuf.buffer, 0, PACKET_BUFFER_SIZE);
memcpy(udp_lbuf.buffer, udp_gbuf.buffer, udp_gbuf.bytes);
udp_lbuf.bytes = udp_gbuf.bytes;
}
p_size = (unsigned char)*udp_lbuf.buffer;
while ((udp_lbuf.bytes == 0) || (udp_lbuf.bytes < p_size)) // Read until we have a full packet
{
// Set the timeout period
udp_select_timeout.tv_sec = IS_TIMEOUT;
udp_select_timeout.tv_usec = 0;
// Clear them
FD_ZERO(&udp_readfd);
FD_ZERO(&udp_exceptfd);
// Set them to watch our socket for data to read and exceptions that maybe thrown
FD_SET(sockudp, &udp_readfd);
FD_SET(sockudp, &udp_exceptfd);
int rc = select(sockudp + 1, &udp_readfd, NULL, &udp_exceptfd, &udp_select_timeout);
if (rc == 0) // Timeout
continue;
if (rc < 0) // An error occured
return -1;
if (FD_ISSET(sockudp, &udp_readfd)) // We got data!
{
// Recieve any waiting bytes
int retval = recv(sockudp, udp_lbuf.buffer + udp_lbuf.bytes, PACKET_BUFFER_SIZE - udp_lbuf.bytes, 0);
// Deal with the results
if (retval < 0) // An error ocurred
{
return -1;
}
p_size = *udp_lbuf.buffer;
udp_lbuf.bytes += retval;
}
else if (FD_ISSET(sockudp, &udp_exceptfd)) // An exception occured - we want to quit
return -1;
}
memset(udp_packet, 0, PACKET_MAX_SIZE);
memcpy(udp_packet, udp_lbuf.buffer, p_size);
logfile << "UDP, ISP_" << (int)udp_peek_packet() << ", " << (float)clock()/CLOCKS_PER_SEC << ", recv" << endl;
return 0;
}
struct IS_BTN // BuTtoN - button header - followed by 0 to 240 characters
{
byte Size; // 12 + TEXT_SIZE (a multiple of 4)
byte Type; // ISP_BTN
byte ReqI; // non-zero (returned in IS_BTC and IS_BTT packets)
byte UCID; // connection to display the button (0 = local / 255 = all)
byte ClickID; // button ID (0 to 239)
byte Inst; // some extra flags - see below
byte BStyle; // button style flags - see below
[COLOR=Red][B]byte TypeIn; // max chars to type in - see below[/B][/COLOR]
byte L; // left : 0 - 200
byte T; // top : 0 - 200
byte W; // width : 0 - 200
byte H; // height : 0 - 200
char Text[TEXT_SIZE]; // 0 to 240 characters of text
};
// ClickID byte : this value is returned in IS_BTC and IS_BTT packets.
// Host buttons and local buttons are stored separately, so there is no chance of a conflict between
// a host control system and a local system (although the buttons could overlap on screen).
// Programmers of local InSim programs may wish to consider using a configurable button range and
// possibly screen position, in case their users will use more than one local InSim program at once.
[COLOR=Blue]// TypeIn byte : if set, the user can click this button to type in text.
// Lowest 7 bits are the maximum number of characters to type in (0 to 95)
// Highest bit (128) can be set to initialise dialog with the button's text
// On clicking the button, a text entry dialog will be opened, allowing the specified number of
// characters to be typed in. The caption on the text entry dialog is optionally customisable using
// Text in the IS_BTN packet. If the first character of IS_BTN's Text field is zero, LFS will read
// the caption up to the second zero. The visible button text then follows that second zero.
// Text : 0-65-66-0 would display button text "AB" and no caption
// Text : 0-65-66-67-0-68-69-70-71-0-0-0 would display button text "DEFG" and caption "ABC"[/COLOR]
// If the TypeIn byte is set in IS_BTN the user can type text into the button
// In that case no IS_BTC is sent - an IS_BTT is sent when the user presses ENTER
struct IS_BTT // BuTton Type - sent back when user types into a text entry button
{
byte Size; // 104
byte Type; // ISP_BTT
byte ReqI; // ReqI as received in the IS_BTN
byte UCID; // connection that typed into the button (zero if local)
byte ClickID; // button identifier originally sent in IS_BTN
byte Inst; // used internally by InSim
byte TypeIn; // from original button specification
byte Sp3;
char Text[96]; // typed text, zero to TypeIn specified in IS_BTN
};
TCP, ISP_ISI, 0.046, send <-- InSim Initialization (ReqI != 0)
TCP, ISP_VER, 0.093, recv <-- IS_VER requested within IS_ISI
TCP, ISP_TINY, 0.109, send <-- TINY_NCN, asking for all connections
TCP, ISP_TINY, 0.109, send <-- TINY_NPL, asking for all players in grid
TCP, ISP_NCN, 0.125, recv <-- Reply to previous TINY_NCN (host is connected)
TCP, ISP_MSO, 8.578, recv <-- Text ("new guest connecting")
TCP, ISP_MSO, 9.062, recv <-- Text ("makakazo connected")
TCP, ISP_NCN, 9.062, recv <-- New player connected
TCP, ISP_MTC, 9.062, send <-- Welcome message to new player
TCP, ISP_BTN, 9.062, send <-- Buttons for X, Y, Z coordinates (this is the purpose of this example application)
TCP, ISP_BTN, 9.062, send
TCP, ISP_BTN, 9.062, send
TCP, ISP_STA, 9.109, recv <-- Race state
TCP, ISP_STA, 13.25, recv
TCP, ISP_NPL, 13.625, recv <-- Player added to grid
TCP, ISP_REO, 14.765, recv <-- Race restarting
TCP, ISP_RST, 14.765, recv <-- Race restarting
TCP, ISP_STA, 14.781, recv <-- Race state
TCP, ISP_MCI, 15.984, recv <-- MCI packets every one second
TCP, ISP_MCI, 16.984, recv
TCP, ISP_MCI, 17.984, recv
TCP, ISP_MCI, 18.984, recv
TCP, ISP_MCI, 19.984, recv
TCP, ISP_MCI, 20.984, recv
TCP, ISP_VTN, 21.89, recv <-- Player votes to end race
TCP, ISP_MSO, 21.906, recv
TCP, ISP_SMALL, 21.906, recv
TCP, ISP_MCI, 21.984, recv
TCP, ISP_MCI, 22.984, recv
TCP, ISP_MCI, 23.968, recv
TCP, ISP_TINY, 24.875, recv
TCP, ISP_TINY, 24.89, recv
TCP, ISP_STA, 24.921, recv
TCP, ISP_TINY, 30.093, recv
TCP, ISP_TINY, 30.093, send <-- Probably keepalive packet
TCP, ISP_MSO, 34.234, recv
TCP, ISP_MST, 34.234, send <-- "!exit"
LFS DEDICATED HOST : 0.5Z
?
?
?
?
?
?
?
?
?
?
?
?
load bans
load font
-----
create english file
initialisations
tables
load objects
start intro
Aston
end of initialisation
Track loaded
Handicaps :
InSim - TCP : X-Y-Z Pos
Host Auth : 10.0.0.8
Send Track : 10.0.0.8
Alive : 10.0.0.8
Connecting guest still alive
Send Track : 10.0.0.8
Connect : 10.0.0.8
Sent OK to new guest
Sent scrutineering packet
Told guests about new guest
A new guest is connecting
Sent guest list to new guest
Sent new user packet
[Q3] makakazo^L connected (makakazo^L)
[Q3] makakazo^L voted to END RACE
Exit : clean up
Exit : delete
[Q3] makakazo : ^L!exit
InSim guest closed : X-Y-Z Pos
shutting down
free objects
free languages
free humans
free font
free host list
kill graphics
save config
free mouse
EXIT
UDP, ISP_ISI, 0.015, send
UDP, ISP_VER, 0.046, recv
UDP, ISP_TINY, 0.046, send
UDP, ISP_TINY, 0.046, send
UDP, ISP_NCN, 0.078, recv
UDP, ISP_MSO, 6.671, recv
UDP, ISP_MSO, 7.203, recv
UDP, ISP_NCN, 7.203, recv
UDP, ISP_MTC, 7.218, send
UDP, ISP_BTN, 7.218, send
UDP, ISP_BTN, 7.218, send
UDP, ISP_BTN, 7.218, send
UDP, ISP_STA, 7.265, recv
UDP, ISP_STA, 9.312, recv
UDP, ISP_NPL, 9.343, recv
UDP, ISP_REO, 11.375, recv
UDP, ISP_RST, 11.375, recv
UDP, ISP_STA, 11.375, recv
UDP, ISP_MCI, 12.578, recv
UDP, ISP_MCI, 13.578, recv
UDP, ISP_MCI, 14.593, recv
UDP, ISP_MCI, 15.593, recv
UDP, ISP_VTN, 15.75, recv
UDP, ISP_MSO, 15.75, recv
UDP, ISP_SMALL, 15.75, recv
UDP, ISP_MCI, 16.593, recv
UDP, ISP_MCI, 17.578, recv
UDP, ISP_MCI, 18.593, recv
UDP, ISP_TINY, 18.734, recv
UDP, ISP_TINY, 18.765, recv
UDP, ISP_STA, 18.781, recv
UDP, ISP_MSO, 22.718, recv
UDP, ISP_MST, 22.734, send
LFS DEDICATED HOST : 0.5Z
?
?
?
?
?
?
?
?
?
?
?
?
load bans
load font
-----
create english file
initialisations
tables
load objects
start intro
Aston
end of initialisation
Track loaded
Handicaps :
InSim - UDP : X-Y-Z Pos
Host Auth : 10.0.0.8
Send Track : 10.0.0.8
Connect : 10.0.0.8
Sent OK to new guest
Sent scrutineering packet
Told guests about new guest
A new guest is connecting
Sent guest list to new guest
Sent new user packet
[Q3] makakazo^L connected (makakazo^L)
[Q3] makakazo^L voted to END RACE
Exit : clean up
Exit : delete
[Q3] makakazo : ^L!exit
host : ^L* Closing application (requested by admin) *
InSim : unknown packet (80 bytes) - X-Y-Z Pos
InSim - UDP : no slots available
InSim - UDP : no slots available
InSim - UDP : no slots available
InSim - UDP : no slots available
InSim timeout : X-Y-Z Pos
shutting down
free objects
free languages
free humans
free font
free host list
kill graphics
save config
free mouse
EXIT
TCP, ISP_ISI, 0.031, send
TCP, ISP_VER, 0.031, recv
TCP, ISP_TINY, 0.031, send
TCP, ISP_TINY, 0.031, send
TCP, ISP_NCN, 0.062, recv
TCP, ISP_MSO, 4.484, recv
TCP, ISP_MSO, 4.984, recv
TCP, ISP_NCN, 4.984, recv
TCP, ISP_MTC, 4.984, send
TCP, ISP_BTN, 4.984, send
TCP, ISP_BTN, 4.984, send
TCP, ISP_BTN, 4.984, send
TCP, ISP_STA, 5.046, recv
TCP, ISP_STA, 6.593, recv
TCP, ISP_NPL, 6.64, recv
TCP, ISP_REO, 9.171, recv <-- Race restarting
TCP, ISP_RST, 9.171, recv <-- Race restarting
TCP, ISP_STA, 9.187, recv <-- Race state
TCP, ISP_VTN, 14.953, recv <-- Player votes to end race
TCP, ISP_MSO, 14.968, recv
TCP, ISP_SMALL, 14.968, recv
TCP, ISP_TINY, 17.937, recv
TCP, ISP_TINY, 17.968, recv
TCP, ISP_STA, 18, recv
TCP, ISP_MSO, 21.125, recv
TCP, ISP_MST, 21.125, send
LFS DEDICATED HOST : 0.5Z
?
?
?
?
?
?
?
?
?
?
?
?
load bans
load font
-----
create english file
initialisations
tables
load objects
start intro
Aston
end of initialisation
Track loaded
Handicaps :
InSim - TCP : X-Y-Z Pos (UDP port 60000)
Host Auth : 10.0.0.8
Send Track : 10.0.0.8
Connect : 10.0.0.8
Sent OK to new guest
Sent scrutineering packet
Told guests about new guest
A new guest is connecting
Sent guest list to new guest
Sent new user packet
[Q3] makakazo^L connected (makakazo^L)
InSim - UDP : no slots available
InSim - UDP : no slots available
InSim - UDP : no slots available
InSim - UDP : no slots available
InSim - UDP : no slots available
[Q3] makakazo^L voted to END RACE
InSim - UDP : no slots available
InSim - UDP : no slots available
InSim - UDP : no slots available
Exit : clean up
Exit : delete
[Q3] makakazo : ^L!exit
InSim guest closed : X-Y-Z Pos
shutting down
free objects
free languages
free humans
free font
free host list
kill graphics
save config
free mouse
EXIT