//#define USE_IPV6 #include #ifdef WIN32 #include #include #include #define MSG_NOSIGNAL 0 #else //#include #include //#include #endif #include #include #include #include #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); }