#include #include #include #include #include #include #include #include #include #include #include #include "protocol.h" #define BACKUPTICS 12 #define NCMD_EXIT 0x80000000 #define NCMD_RETRANSMIT 0x40000000 #define NCMD_SETUP 0x20000000 #define NCMD_KILL 0x10000000 // kill game #define NCMD_CHECKSUM 0x0fffffff typedef struct { short gameid; // so multiple games can setup at once short drone; short nodesfound; short nodeswanted; } setupdata_t; typedef struct a { // High bit is retransmit request. unsigned checksum; // Only valid if NCMD_RETRANSMIT. byte retransmitfrom; byte starttic; byte player; byte numtics; ticcmd_t cmds[BACKUPTICS]; } doomdata_t; typedef struct { signed int tic; union altu { setupdata_t s; unsigned char data[100]; doomdata_t d; } u; } ipxpacket_t; int nodes; unsigned short port = 0x869b; int ipx_socket(void) { int s = socket(PF_IPX,SOCK_DGRAM,0); struct sockaddr_ipx sa; if (s == -1) { fprintf(stderr,"socket(PF_PIPX): %s\n",strerror(errno)); exit(-1); } memset(&sa,0,sizeof(sa)); memset(sa.sipx_node,0xff,sizeof(sa.sipx_node)); sa.sipx_port = htons(port); if (bind(s,(struct sockaddr*)&sa,sizeof(sa)) == -1) { fprintf(stderr,"bind(%d): %s\n",port,strerror(errno)); exit(-1); } return s; } int udp_socket(const char* ip) { struct sockaddr_in sa; int s = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP); if (s == -1) { fprintf(stderr,"socket(PF_INET): %s\n", strerror(errno)); exit(-1); } sa.sin_family=PF_INET; inet_aton(ip,&sa.sin_addr); sa.sin_port = htons(5030); if (connect(s,(struct sockaddr*)&sa,sizeof sa) == -1) { fprintf(stderr,"connect(%s:%d): %s\n", ip, 5030, strerror(errno)); exit(-1); } return s; } static byte ChecksumPacket(const packet_header_t* buffer, size_t len) { const byte* p = (void*)buffer; byte sum = 0; if (len==0) return 0; while (p++, --len) sum += *p; return sum; } // // Checksum // unsigned NetbufferChecksum (void* p, size_t l) { unsigned c; c = 0x1234567; l = l/4; for (int i=0 ; i= -64 && delta <= 64) return (maketic&~0xff) + low; if (delta > 64) return (maketic&~0xff) - 256 + low; if (delta < -64) return (maketic&~0xff) + 256 + low; fprintf(stderr,"ExpandTics strange value %i at maketic %i\n",low,maketic); exit(-2); } void send_udp_packet(enum packet_type_e type, unsigned tic, void* data, size_t len) { packet_header_t* p = calloc(sizeof(packet_header_t)+len+1,1); p->tic = doom_htonl(basetic = tic); p->type = type; if (!data) { data = (void*)&consoleplayer; len = 1; } memcpy(((char*)p)+sizeof(*p),data,len); p->checksum = ChecksumPacket(p,sizeof(packet_header_t)+len); write(udps,p,sizeof(packet_header_t)+len+1); } int connected; int ipxcounter; void ipx_receive(int s) { ipxpacket_t buf; int rc; struct sockaddr from; size_t sl = sizeof(from); rc = recvfrom(s,&buf,sizeof buf,0,&from,&sl); if (rc == -1) { fprintf(stderr,"read(ipx): %s\n", strerror(errno)); exit(-2); } if (rc > 0) { if (buf.tic == -1) { // Setup packet if (!connected++) { connect(s,&from,sl); send_udp_packet(PKT_INIT,0,NULL,0); } } else { if (buf.u.d.checksum & NCMD_SETUP) { printf("setup packet, dropped\n"); } else if (buf.u.d.checksum & NCMD_EXIT) { send_udp_packet(PKT_QUIT,buf.u.d.starttic,NULL,0); exit(0); } else if ((buf.u.d.checksum & NCMD_CHECKSUM) == buf.u.d.checksum) { // No flags, normal game packet char outbuf[100]; int tics; outbuf[0] = tics = buf.u.d.numtics; outbuf[1] = buf.u.d.player; for (int i=0; i< tics; i++) TicToRaw(outbuf+2+i*sizeof(ticcmd_t),&buf.u.d.cmds[i]); send_udp_packet(PKT_TICC, ExpandTics(buf.u.d.starttic, basetic), outbuf, 2+tics*sizeof(ticcmd_t)); } } } } void udp_receive(int s) { size_t len = 1024; packet_header_t *p = malloc(len); int rc; rc = read(s,p,len); if (rc < 0) { fprintf(stderr,"read(udp): %s\n", strerror(errno)); exit(-2); } if (rc > 0) { switch (p->type) { case PKT_SETUP: { struct setup_packet_s *sinfo = (void*)(p+1); consoleplayer = sinfo->yourplayer; send_udp_packet(PKT_GO,0,NULL,0); write(ipxs,"\xff\xff\xff\xff\x00\x00\x00\x00\x02\x00\x02\x00\x00\x00\x00\x00",16); } break; case PKT_GO: { ipxpacket_t pkt; memset(&pkt,0,sizeof(pkt)); pkt.tic = ipxcounter++; pkt.u.d.player = consoleplayer^1; pkt.u.d.starttic = 0; pkt.u.d.numtics = 0; pkt.u.d.retransmitfrom = 0; pkt.u.d.checksum = NetbufferChecksum(&pkt.u.d.retransmitfrom, 4); write(ipxs,&pkt,16); } break; case PKT_TICS: { ipxpacket_t pkt; int tic = doom_ntohl(p->tic); byte *pp = (void*)(p+1); int tics = *pp++; memset(&pkt,0,sizeof(pkt)); size_t len; pkt.tic = ipxcounter++; pkt.u.d.starttic = tic; pkt.u.d.player = (consoleplayer == 0 ? 1 : 0); pkt.u.d.numtics = tics; for (int t=0; t0) { if (FD_ISSET(ipxs,&fds)) ipx_receive(ipxs); if (FD_ISSET(udps,&fds)) udp_receive(udps); } } } int main(int argc, char**argv) { ipxs = ipx_socket(); udps = udp_socket(argv[1]); loop(ipxs,udps); return 0; }