lfsdash/insim.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);
}