440 lines
12 KiB
C
440 lines
12 KiB
C
//#define USE_IPV6
|
|
|
|
#include <stdlib.h>
|
|
#ifdef WIN32
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#include <windows.h>
|
|
#define MSG_NOSIGNAL 0
|
|
#else
|
|
//#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
//#include <netinet/in.h>
|
|
#endif
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "cars.h"
|
|
#include "socket.h"
|
|
#include "insim.h"
|
|
#include "network_worker.h"
|
|
#include "queue.h"
|
|
#include "outgauge.h"
|
|
|
|
struct conninfo s_conninfo[256];
|
|
struct carinfo s_carinfo[256];
|
|
|
|
struct insim_buffer {
|
|
int state;
|
|
uint8_t buffer[65536];
|
|
int pos;
|
|
int offset;
|
|
};
|
|
|
|
static struct insim_buffer s_insim_tcp;
|
|
static struct insim_buffer s_insim_udp;
|
|
|
|
static struct queue_t *s_send_queue;
|
|
static uint8_t *s_send_packet;
|
|
static uint8_t s_send_pos;
|
|
|
|
void insim_queue(const void *buffer, size_t size)
|
|
{
|
|
uint8_t *packet = malloc(size);
|
|
memcpy(packet, buffer, size);
|
|
queue_produce(s_send_queue, packet);
|
|
}
|
|
|
|
int insim_dequeue(int fd)
|
|
{
|
|
if (s_send_packet == NULL) queue_consume(s_send_queue, (void *)&s_send_packet);
|
|
|
|
if (s_send_packet != NULL)
|
|
{
|
|
ssize_t s = send(fd, (char *)s_send_packet + s_send_pos, s_send_packet[0] - s_send_pos, MSG_NOSIGNAL);
|
|
if (s >= 0) s_send_pos += s;
|
|
if (s_send_pos >= s_send_packet[0])
|
|
{
|
|
free(s_send_packet);
|
|
s_send_packet = 0;
|
|
s_send_pos = 0;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void insim_request_info(void)
|
|
{
|
|
memset(s_conninfo, 0, sizeof s_conninfo);
|
|
memset(s_carinfo, 0, sizeof s_carinfo);
|
|
|
|
struct IS_TINY p;
|
|
p.Size = sizeof p;
|
|
p.Type = ISP_TINY;
|
|
p.ReqI = 2;
|
|
p.SubT = TINY_NCN;
|
|
insim_queue(&p, sizeof p);
|
|
|
|
p.Size = sizeof p;
|
|
p.Type = ISP_TINY;
|
|
p.ReqI = 3;
|
|
p.SubT = TINY_NPL;
|
|
insim_queue(&p, sizeof p);
|
|
}
|
|
|
|
void insim_handle_tiny(int fd, const void *buffer)
|
|
{
|
|
const struct IS_TINY *p = buffer;
|
|
switch (p->SubT)
|
|
{
|
|
case TINY_NONE:
|
|
{
|
|
struct IS_TINY r;
|
|
r.Size = sizeof r;
|
|
r.Type = ISP_TINY;
|
|
r.ReqI = 0;
|
|
r.SubT = TINY_NONE;
|
|
insim_queue(&r, sizeof r);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ltc_duty(const char *uname, int state)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 256; i++) {
|
|
if (!strcmp(uname, s_conninfo[i].uname)) {
|
|
s_conninfo[i].state = state;
|
|
printf("Player %s state %d\n", uname, state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void insim_handle_ism(int fd, const void *buffer)
|
|
{
|
|
const struct IS_ISM *p = buffer;
|
|
|
|
printf("Joined %s\n", p->HName);
|
|
insim_request_info();
|
|
}
|
|
|
|
void insim_handle_mso(int fd, const void *buffer)
|
|
{
|
|
return;
|
|
const struct IS_MSO *p = buffer;
|
|
printf("%d %d %d %d %s\n", p->UCID, p->PLID, p->UserType, p->TextStart, p->Msg);
|
|
|
|
char buf[128], *bufp = buf;
|
|
strncpy(buf, p->Msg, sizeof buf);
|
|
|
|
int state = 0;
|
|
char *name = NULL;
|
|
|
|
int i;
|
|
for (i = 0; i < 10; i++) {
|
|
char *a = strsep(&bufp, " ");
|
|
if (a == NULL) return;
|
|
if (i == 0 && strcmp(a, "***")) return;
|
|
if (i == 1 && (!strcmp(a, "Officer") || !strcmp(a, "Cadet"))) state |= 1;
|
|
if (i == 2 && state == 1) name = a;
|
|
if (i == 3 && strcmp(a, "has")) return;
|
|
if (i == 4 && strcmp(a, "gone")) return;
|
|
if (i == 5 && !strcmp(a, "onduty")) ltc_duty(name, 1);
|
|
if (i == 5 && !strcmp(a, "offduty")) ltc_duty(name, 0);
|
|
}
|
|
}
|
|
|
|
void insim_handle_ncn(int fd, const void *buffer)
|
|
{
|
|
const struct IS_NCN *p = buffer;
|
|
struct conninfo *c = s_conninfo + p->UCID;
|
|
memset(c, 0, sizeof *c);
|
|
c->active = 1;
|
|
strncpy(c->uname, p->UName, sizeof c->uname);
|
|
strncpy(c->pname, p->PName, sizeof c->pname);
|
|
c->admin = p->Admin;
|
|
|
|
printf("Know about connection %s (%s) %d\n", c->pname, c->uname, c->admin);
|
|
}
|
|
|
|
void insim_handle_cnl(int fd, const void *buffer)
|
|
{
|
|
const struct IS_CNL *p = buffer;
|
|
struct conninfo *c = s_conninfo + p->UCID;
|
|
c->active = 0;
|
|
}
|
|
|
|
void insim_handle_cpr(int fd, const void *buffer)
|
|
{
|
|
const struct IS_CPR *p = buffer;
|
|
struct conninfo *c = s_conninfo + p->UCID;
|
|
if (c->active == 0) fprintf(stderr, "Rename of inactive connection\n");
|
|
strncpy(c->pname, p->PName, sizeof c->pname);
|
|
}
|
|
|
|
void insim_handle_npl(int fd, const void *buffer)
|
|
{
|
|
const struct IS_NPL *p = buffer;
|
|
struct carinfo *car = s_carinfo + p->PLID;
|
|
memset(car, 0, sizeof *car);
|
|
car->active = 1;
|
|
//strncpy(car->cname, p->CName, sizeof car->cname);
|
|
car->ucid = p->UCID;
|
|
car->mass = p->H_Mass;
|
|
car->intake = p->H_TRes;
|
|
|
|
strncpy(car->cname, find_car(p->CName, car->intake, car->mass), sizeof car->cname);
|
|
|
|
printf("Car %d joined (%s) - %s\n", p->PLID, car->cname, s_conninfo[car->ucid].uname);
|
|
}
|
|
|
|
void insim_handle_pll(int fd, const void *buffer)
|
|
{
|
|
const struct IS_PLL *p = buffer;
|
|
struct carinfo *car = s_carinfo + p->PLID;
|
|
car->active = 0;
|
|
printf("Car %d left - %s\n", p->PLID, s_conninfo[car->ucid].uname);
|
|
}
|
|
|
|
void insim_handle_mci(int fd, const void *buffer)
|
|
{
|
|
const struct IS_MCI *p = buffer;
|
|
int i;
|
|
for (i = 0; i < p->NumC; i++)
|
|
{
|
|
const struct CompCar *c = p->Info + i;
|
|
struct carinfo *car = s_carinfo + c->PLID;
|
|
|
|
car->position = c->Position;
|
|
car->info = c->Info;
|
|
car->sp3 = c->Sp3;
|
|
car->x = c->X;
|
|
car->y = c->Y;
|
|
car->z = c->Z;
|
|
car->speed = c->Speed;
|
|
car->direction = c->Direction;
|
|
car->heading = c->Heading;
|
|
car->angvel = c->AngVel;
|
|
|
|
car->hist_x[car->hist] = c->X;
|
|
car->hist_y[car->hist] = c->Y;
|
|
car->hist_s[car->hist] = c->Speed;
|
|
car->hist = (car->hist + 1) % CARPATHSIZE;
|
|
}
|
|
}
|
|
|
|
typedef void (*insim_handler)(int fd, const void *buffer);
|
|
|
|
static const insim_handler s_handlers[] =
|
|
{
|
|
NULL, // ISP_NONE // 0 : not used
|
|
NULL, // ISP_ISI // 1 - instruction : insim initialise
|
|
NULL, // ISP_VER // 2 - info : version info
|
|
insim_handle_tiny, // ISP_TINY // 3 - both ways : multi purpose
|
|
NULL, // ISP_SMALL // 4 - both ways : multi purpose
|
|
NULL, // ISP_STA // 5 - info : state info
|
|
NULL, // ISP_SCH // 6 - instruction : single character
|
|
NULL, // ISP_SFP // 7 - instruction : state flags pack
|
|
NULL, // ISP_SCC // 8 - instruction : set car camera
|
|
NULL, // ISP_CPP // 9 - both ways : cam pos pack
|
|
insim_handle_ism, // ISP_ISM // 10 - info : start multiplayer
|
|
insim_handle_mso, // ISP_MSO // 11 - info : message out
|
|
NULL, // ISP_III // 12 - info : hidden /i message
|
|
NULL, // ISP_MST // 13 - instruction : type message or /command
|
|
NULL, // ISP_MTC // 14 - instruction : message to a connection
|
|
NULL, // ISP_MOD // 15 - instruction : set screen mode
|
|
NULL, // ISP_VTN // 16 - info : vote notification
|
|
NULL, // ISP_RST // 17 - info : race start
|
|
insim_handle_ncn, // ISP_NCN // 18 - info : new connection
|
|
insim_handle_cnl, // ISP_CNL // 19 - info : connection left
|
|
insim_handle_cpr, // ISP_CPR // 20 - info : connection renamed
|
|
insim_handle_npl, // ISP_NPL // 21 - info : new player (joined race)
|
|
NULL, // ISP_PLP // 22 - info : player pit (keeps slot in race)
|
|
insim_handle_pll, // ISP_PLL // 23 - info : player leave (spectate - loses slot)
|
|
NULL, // ISP_LAP // 24 - info : lap time
|
|
NULL, // ISP_SPX // 25 - info : split x time
|
|
NULL, // ISP_PIT // 26 - info : pit stop start
|
|
NULL, // ISP_PSF // 27 - info : pit stop finish
|
|
NULL, // ISP_PLA // 28 - info : pit lane enter / leave
|
|
NULL, // ISP_CCH // 29 - info : camera changed
|
|
NULL, // ISP_PEN // 30 - info : penalty given or cleared
|
|
NULL, // ISP_TOC // 31 - info : take over car
|
|
NULL, // ISP_FLG // 32 - info : flag (yellow or blue)
|
|
NULL, // ISP_PFL // 33 - info : player flags (help flags)
|
|
NULL, // ISP_FIN // 34 - info : finished race
|
|
NULL, // ISP_RES // 35 - info : result confirmed
|
|
NULL, // ISP_REO // 36 - both ways : reorder (info or instruction)
|
|
NULL, // ISP_NLP // 37 - info : node and lap packet
|
|
insim_handle_mci, // ISP_MCI // 38 - info : multi car info
|
|
NULL, // ISP_MSX // 39 - instruction : type message
|
|
NULL, // ISP_MSL // 40 - instruction : message to local computer
|
|
NULL, // ISP_CRS // 41 - info : car reset
|
|
NULL, // ISP_BFN // 42 - both ways : delete buttons / receive button requests
|
|
NULL, // ISP_AXI // 43 - info : autocross layout information
|
|
NULL, // ISP_AXO // 44 - info : hit an autocross object
|
|
NULL, // ISP_BTN // 45 - instruction : show a button on local or remote screen
|
|
NULL, // ISP_BTC // 46 - info : sent when a user clicks a button
|
|
NULL, // ISP_BTT // 47 - info : sent after typing into a button
|
|
NULL, // ISP_RIP // 48 - both ways : replay information packet
|
|
NULL, // ISP_SSH // 49 - both ways : screenshot
|
|
NULL, // ISP_CON // 50 - info : contact between cars (collision report)
|
|
NULL, // ISP_OBH // 51 - info : contact car + object (collision report)
|
|
NULL, // ISP_HLV // 52 - info : report incidents that would violate HLVC
|
|
NULL, // ISP_PLC // 53 - instruction : player cars
|
|
NULL, // ISP_AXM // 54 - both ways : autocross multiple objects
|
|
NULL, // ISP_ACR // 55 - info : admin command report
|
|
};
|
|
|
|
void insim_handle(int fd, const uint8_t *buffer)
|
|
{
|
|
uint8_t type = buffer[1];
|
|
if (type <= ISP_ACR && s_handlers[type] != NULL)
|
|
{
|
|
s_handlers[type](fd, buffer);
|
|
}
|
|
}
|
|
|
|
int insim_recv(int fd, int can_write, int can_read, void *arg)
|
|
{
|
|
struct insim_buffer *buffer = arg;
|
|
|
|
if (can_write && buffer->state == 1)
|
|
{
|
|
insim_dequeue(fd);
|
|
}
|
|
if (can_read)
|
|
{
|
|
if (buffer->pos >= sizeof buffer->buffer)
|
|
{
|
|
printf("buffer full\n");
|
|
return 0;
|
|
}
|
|
ssize_t res = recv(fd, (char *)buffer->buffer + buffer->pos, sizeof buffer->buffer - buffer->pos, 0);
|
|
if (res == -1)
|
|
{
|
|
#ifdef WIN32
|
|
if (WSAGetLastError() == WSAECONNRESET)
|
|
#else
|
|
if (errno == ECONNRESET)
|
|
#endif
|
|
{
|
|
printf("connreset\n");
|
|
return 0;
|
|
}
|
|
#ifdef WIN32
|
|
else if (WSAGetLastError() != WSAEWOULDBLOCK)
|
|
#else
|
|
else if (errno != EWOULDBLOCK && errno != EAGAIN)
|
|
#endif
|
|
{
|
|
printf("hmm: %s\n", strerror(errno));
|
|
return 0;
|
|
}
|
|
}
|
|
if (res == 0)
|
|
{
|
|
printf("nothing received\n");
|
|
return 0;
|
|
}
|
|
|
|
buffer->pos += res;
|
|
|
|
while (1)
|
|
{
|
|
uint8_t size = buffer->buffer[buffer->offset];
|
|
if (size % 4 != 0)
|
|
{
|
|
printf("invalid packet, size is %d\n", size);
|
|
return 0;
|
|
}
|
|
|
|
if (buffer->pos - buffer->offset >= size)
|
|
{
|
|
insim_handle(fd, buffer->buffer + buffer->offset);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (buffer->pos - buffer->offset > size)
|
|
{
|
|
buffer->offset += size;
|
|
}
|
|
else
|
|
{
|
|
buffer->pos = 0;
|
|
buffer->offset = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
/*
|
|
#ifdef USE_IPV6
|
|
static struct sockaddr_in6 serv_addr;
|
|
#else
|
|
static struct sockaddr_in serv_addr;
|
|
#endif
|
|
*/
|
|
|
|
extern int g_outgauge_port;
|
|
extern int g_insim_port;
|
|
|
|
static void insim_connect(int fd, void *arg)
|
|
{
|
|
if (fd == -1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
s_insim_tcp.state = 1;
|
|
s_insim_tcp.pos = 0;
|
|
s_insim_tcp.offset = 0;
|
|
register_socket(fd, SM_READ | SM_WRITE, &insim_recv, &s_insim_tcp);
|
|
|
|
fd = network_listen(g_insim_port + 1, 0);
|
|
s_insim_udp.state = 2;
|
|
s_insim_udp.pos = 0;
|
|
s_insim_udp.offset = 0;
|
|
register_socket(fd, SM_READ, &insim_recv, &s_insim_udp);
|
|
|
|
struct IS_ISI p;
|
|
memset(&p, 0, sizeof p);
|
|
p.Size = sizeof p;
|
|
p.Type = ISP_ISI;
|
|
p.ReqI = 0;
|
|
p.Zero = 0;
|
|
p.UDPPort = g_outgauge_port; //g_insim_port + 1;
|
|
p.Flags = ISF_LOCAL;
|
|
p.Sp0 = 0;
|
|
p.Prefix = 0;
|
|
p.Interval = 100;
|
|
snprintf(p.Admin, sizeof p.Admin, "");
|
|
snprintf(p.IName, sizeof p.IName, "LFS Dashboard");
|
|
insim_queue(&p, sizeof p);
|
|
|
|
struct IS_SMALL s;
|
|
s.Size = sizeof s;
|
|
s.Type = ISP_SMALL;
|
|
s.ReqI = 1;
|
|
s.SubT = SMALL_SSG;
|
|
s.UVal = 1;
|
|
insim_queue(&s, sizeof s);
|
|
|
|
insim_request_info();
|
|
}
|
|
|
|
void insim_init(const char *hostname, int port)
|
|
{
|
|
s_insim_tcp.state = 0;
|
|
s_insim_udp.state = 0;
|
|
s_send_queue = queue_new();
|
|
network_connect(hostname, port, &insim_connect, NULL);
|
|
}
|