// resend the data if needed void Net_AckTicker(void) { #ifndef NONET INT32 i; for (i = 0; i < MAXACKPACKETS; i++) { const INT32 nodei = ackpak[i].destinationnode; node_t *node = &nodes[nodei]; #ifdef NEWPING if (ackpak[i].acknum && ackpak[i].senttime + NODETIMEOUT < I_GetTime()) #else if (ackpak[i].acknum && ackpak[i].senttime + node->timeout < I_GetTime()) #endif { if (ackpak[i].resentnum > 10 && (node->flags & CLOSE)) { DEBFILE(va("ack %d sent 10 times so connection is supposed lost: node %d\n", i, nodei)); Net_CloseConnection(nodei | FORCECLOSE); ackpak[i].acknum = 0; continue; } #ifdef NEWPING DEBFILE(va("Resend ack %d, %u<%d at %u\n", ackpak[i].acknum, ackpak[i].senttime, NODETIMEOUT, I_GetTime())); #else DEBFILE(va("Resend ack %d, %u<%d at %u\n", ackpak[i].acknum, ackpak[i].senttime, node->timeout, I_GetTime())); #endif M_Memcpy(netbuffer, ackpak[i].pak.raw, ackpak[i].length); ackpak[i].senttime = I_GetTime(); ackpak[i].resentnum++; ackpak[i].nextacknum = node->nextacknum; retransmit++; // for stat HSendPacket((INT32)(node - nodes), false, ackpak[i].acknum, (size_t)(ackpak[i].length - BASEPACKETSIZE)); } } for (i = 1; i < MAXNETNODES; i++) { // this is something like node open flag if (nodes[i].firstacktosend) { // we haven't sent a packet for a long time // acknowledge packet if needed if (nodes[i].lasttimeacktosend_sent + ACKTOSENDTIMEOUT < I_GetTime()) Net_SendAcks(i); if (!(nodes[i].flags & CLOSE) && nodes[i].lasttimepacketreceived + connectiontimeout < I_GetTime()) { Net_ConnectionTimeout(i); } } } #endif }
static void NET_Get(void) { INT32 mystatus; INT32 newnode; mypacket.len = MAXPACKETLENGTH; if (!NET_CanGet()) { doomcom->remotenode = -1; // no packet return; } mystatus = SDLNet_UDP_Recv(mysocket,&mypacket); if (mystatus != -1) { if (mypacket.channel != -1) { doomcom->remotenode = mypacket.channel+1; // good packet from a game player doomcom->datalength = mypacket.len; return; } newnode = SDLNet_UDP_Bind(mysocket,-1,&mypacket.address); if (newnode != -1) { size_t i; newnode++; M_Memcpy(&clientaddress[newnode], &mypacket.address, sizeof (IPaddress)); DEBFILE(va("New node detected: node:%d address:%s\n", newnode, NET_GetNodeAddress(newnode))); doomcom->remotenode = newnode; // good packet from a game player doomcom->datalength = mypacket.len; for (i = 0; i < numbans; i++) { if (NET_cmpaddr(&mypacket.address, &banned[i])) { DEBFILE("This dude has been banned\n"); NET_bannednode[newnode] = true; break; } } if (i == numbans) NET_bannednode[newnode] = false; return; } else CONS_Printf("SDL_Net: %s",SDLNet_GetError()); } else if (mystatus == -1) { CONS_Printf("SDL_Net: %s",SDLNet_GetError()); } DEBFILE("New node detected: No more free slots\n"); doomcom->remotenode = -1; // no packet }
// remove last packet received ack before resending the ackret // (the higher layer doesn't have room, or something else ....) void Net_UnAcknowledgPacket(INT32 node) { INT32 hm1 = (nodes[node].acktosend_head-1+MAXACKTOSEND) % MAXACKTOSEND; DEBFILE(va("UnAcknowledge node %d\n", node)); if (!node) return; if (nodes[node].acktosend[hm1] == netbuffer->ack) { nodes[node].acktosend[hm1] = 0; nodes[node].acktosend_head = (UINT8)hm1; } else if (nodes[node].firstacktosend == netbuffer->ack) { nodes[node].firstacktosend--; if (!nodes[node].firstacktosend) nodes[node].firstacktosend = UINT8_MAX; } else { while (nodes[node].firstacktosend != netbuffer->ack) { nodes[node].acktosend_tail = (UINT8) ((nodes[node].acktosend_tail-1+MAXACKTOSEND) % MAXACKTOSEND); nodes[node].acktosend[nodes[node].acktosend_tail] = nodes[node].firstacktosend; nodes[node].firstacktosend--; if (!nodes[node].firstacktosend) nodes[node].firstacktosend = UINT8_MAX; } nodes[node].firstacktosend++; if (!nodes[node].firstacktosend) nodes[node].firstacktosend = 1; } }
static void NET_FreeNodenum(INT32 numnode) { // can't disconnect from self :) if (!numnode) return; DEBFILE(va("Free node %d (%s)\n", numnode, NET_GetNodeAddress(numnode))); SDLNet_UDP_Unbind(mysocket,numnode-1); memset(&clientaddress[numnode], 0, sizeof (IPaddress)); }
// return a free acknum and copy netbuffer in the ackpak table static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) { node_t *node = &nodes[doomcom->remotenode]; INT32 i, numfreeslote = 0; if (cmpack((UINT8)((node->remotefirstack + MAXACKTOSEND) % 256), node->nextacknum) < 0) { DEBFILE(va("too fast %d %d\n",node->remotefirstack,node->nextacknum)); return false; } for (i = 0; i < MAXACKPACKETS; i++) if (!ackpak[i].acknum) { // for low priority packet, make sure let freeslotes so urgents packets can be sent numfreeslote++; if (netbuffer->packettype >= PT_CANFAIL && numfreeslote < URGENTFREESLOTENUM) continue; ackpak[i].acknum = node->nextacknum; ackpak[i].nextacknum = node->nextacknum; node->nextacknum++; if (!node->nextacknum) node->nextacknum++; ackpak[i].destinationnode = (UINT8)(node - nodes); ackpak[i].length = doomcom->datalength; if (lowtimer) { // lowtime mean can't be sent now so try it soon as possible ackpak[i].senttime = 0; ackpak[i].resentnum = 1; } else { ackpak[i].senttime = I_GetTime(); ackpak[i].resentnum = 0; } M_Memcpy(ackpak[i].pak.raw, netbuffer, ackpak[i].length); *freeack = ackpak[i].acknum; sendackpacket++; // for stat return true; } #ifdef PARANOIA if (devparm) I_OutputMsg("No more free ackpacket\n"); #endif if (netbuffer->packettype < PT_CANFAIL) I_Error("Connection lost\n"); return false; }
static void SOCK_FreeNodenum(INT32 numnode) { // can't disconnect from self :) if (!numnode || numnode > MAXNETNODES) return; DEBFILE(va("Free node %d (%s)\n", numnode, SOCK_GetNodeAddress(numnode))); nodeconnected[numnode] = false; nodesocket[numnode] = BADSOCKET; // put invalid address memset(&clientaddress[numnode], 0, sizeof (clientaddress[numnode])); }
static void Removeack(INT32 i) { INT32 node = ackpak[i].destinationnode; #ifndef NEWPING fixed_t trueping = (I_GetTime() - ackpak[i].senttime)<<FRACBITS; if (ackpak[i].resentnum) { // +FRACUNIT/2 for round nodes[node].ping = (nodes[node].ping*7 + trueping)/8; nodes[node].varping = (nodes[node].varping*7 + abs(nodes[node].ping-trueping))/8; nodes[node].timeout = TIMEOUT(nodes[node].ping,nodes[node].varping); } DEBFILE(va("Remove ack %d trueping %d ping %f var %f timeout %d\n",ackpak[i].acknum,trueping>>FRACBITS,(double)FIXED_TO_FLOAT(nodes[node].ping),(double)FIXED_TO_FLOAT(nodes[node].varping),nodes[node].timeout)); #else DEBFILE(va("Remove ack %d\n",ackpak[i].acknum)); #endif ackpak[i].acknum = 0; if (nodes[node].flags & CLOSE) Net_CloseConnection(node); }
// // HGetPacket // Returns false if no packet is waiting // Check Datalength and checksum // boolean HGetPacket(void) { // get a packet from self if (rebound_tail != rebound_head) { M_Memcpy(netbuffer, &reboundstore[rebound_tail], reboundsize[rebound_tail]); doomcom->datalength = reboundsize[rebound_tail]; if (netbuffer->packettype == PT_NODETIMEOUT) doomcom->remotenode = netbuffer->u.textcmd[0]; else doomcom->remotenode = 0; rebound_tail = (rebound_tail+1) % MAXREBOUND; #ifdef DEBUGFILE if (debugfile) DebugPrintpacket("GETLOCAL"); #endif return true; } if (!netgame) return false; #ifndef NONET while(true) { I_NetGet(); if (doomcom->remotenode == -1) return false; getbytes += packetheaderlength + doomcom->datalength; // for stat if (doomcom->remotenode >= MAXNETNODES) { DEBFILE(va("receive packet from node %d !\n", doomcom->remotenode)); continue; } nodes[doomcom->remotenode].lasttimepacketreceived = I_GetTime(); if (netbuffer->checksum != NetbufferChecksum()) { DEBFILE("Bad packet checksum\n"); Net_CloseConnection(doomcom->remotenode); continue; } #ifdef DEBUGFILE if (debugfile) DebugPrintpacket("GET"); #endif // proceed the ack and ackreturn field if (!Processackpak()) continue; // discarded (duplicated) // a packet with just ackreturn if (netbuffer->packettype == PT_NOTHING) { GotAcks(); continue; } break; } #endif // ndef NONET return true; }
// // HSendPacket // boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength) { doomcom->datalength = (INT16)(packetlength + BASEPACKETSIZE); if (node == 0) // packet is to go back to us { if ((rebound_head+1) % MAXREBOUND == rebound_tail) { #ifdef PARANOIA CONS_Debug(DBG_NETPLAY, "No more rebound buf\n"); #endif return false; } M_Memcpy(&reboundstore[rebound_head], netbuffer, doomcom->datalength); reboundsize[rebound_head] = doomcom->datalength; rebound_head = (rebound_head+1) % MAXREBOUND; #ifdef DEBUGFILE if (debugfile) { doomcom->remotenode = (INT16)node; DebugPrintpacket("SENDLOCAL"); } #endif return true; } if (!netgame) I_Error("Tried to transmit to another node"); #ifdef NONET (void)node; (void)reliable; (void)acknum; #else // do this before GetFreeAcknum because this function backup // the current packet doomcom->remotenode = (INT16)node; if (doomcom->datalength <= 0) { DEBFILE("HSendPacket: nothing to send\n"); #ifdef DEBUGFILE if (debugfile) DebugPrintpacket("TRISEND"); #endif return false; } if (node < MAXNETNODES) // can be a broadcast netbuffer->ackreturn = GetAcktosend(node); else netbuffer->ackreturn = 0; if (reliable) { if (I_NetCanSend && !I_NetCanSend()) { if (netbuffer->packettype < PT_CANFAIL) GetFreeAcknum(&netbuffer->ack, true); DEBFILE("HSendPacket: Out of bandwidth\n"); return false; } else if (!GetFreeAcknum(&netbuffer->ack, false)) return false; } else netbuffer->ack = acknum; netbuffer->checksum = NetbufferChecksum(); sendbytes += packetheaderlength + doomcom->datalength; // for stat // simulate internet :) if (true || rand()<(INT32)RAND_MAX/5) { #ifdef DEBUGFILE if (debugfile) DebugPrintpacket("SEND"); #endif I_NetSend(); } #ifdef DEBUGFILE else if (debugfile) DebugPrintpacket("NOTSEND"); #endif #endif // ndef NONET return true; }
// we have got a packet proceed the ack request and ack return static boolean Processackpak(void) { INT32 i; boolean goodpacket = true; node_t *node = &nodes[doomcom->remotenode]; // received an ack return, so remove the ack in the list if (netbuffer->ackreturn && cmpack(node->remotefirstack, netbuffer->ackreturn) < 0) { node->remotefirstack = netbuffer->ackreturn; // search the ackbuffer and free it for (i = 0; i < MAXACKPACKETS; i++) if (ackpak[i].acknum && ackpak[i].destinationnode == node - nodes && cmpack(ackpak[i].acknum, netbuffer->ackreturn) <= 0) { Removeack(i); } } // received a packet with ack, queue it to send the ack back if (netbuffer->ack) { UINT8 ack = netbuffer->ack; getackpacket++; if (cmpack(ack, node->firstacktosend) <= 0) { DEBFILE(va("Discard(1) ack %d (duplicated)\n", ack)); duppacket++; goodpacket = false; // discard packet (duplicate) } else { // check if it is not already in the queue for (i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND) if (node->acktosend[i] == ack) { DEBFILE(va("Discard(2) ack %d (duplicated)\n", ack)); duppacket++; goodpacket = false; // discard packet (duplicate) break; } if (goodpacket) { // is a good packet so increment the acknowledge number, // then search for a "hole" in the queue UINT8 nextfirstack = (UINT8)(node->firstacktosend + 1); if (!nextfirstack) nextfirstack = 1; if (ack == nextfirstack) { UINT8 hm1; // head - 1 boolean change = true; node->firstacktosend = nextfirstack++; if (!nextfirstack) nextfirstack = 1; hm1 = (UINT8)((node->acktosend_head-1+MAXACKTOSEND) % MAXACKTOSEND); while (change) { change = false; for (i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND) { if (cmpack(node->acktosend[i], nextfirstack) <= 0) { if (node->acktosend[i] == nextfirstack) { node->firstacktosend = nextfirstack++; if (!nextfirstack) nextfirstack = 1; change = true; } if (i == node->acktosend_tail) { node->acktosend[node->acktosend_tail] = 0; node->acktosend_tail = (UINT8)((i+1) % MAXACKTOSEND); } else if (i == hm1) { node->acktosend[hm1] = 0; node->acktosend_head = hm1; hm1 = (UINT8)((hm1-1+MAXACKTOSEND) % MAXACKTOSEND); } } } } } else // out of order packet { // don't increment firsacktosend, put it in asktosend queue // will be incremented when the nextfirstack comes (code above) UINT8 newhead = (UINT8)((node->acktosend_head+1) % MAXACKTOSEND); DEBFILE(va("out of order packet (%d expected)\n", nextfirstack)); if (newhead != node->acktosend_tail) { node->acktosend[node->acktosend_head] = ack; node->acktosend_head = newhead; } else // buffer full discard packet, sender will resend it { // we can admit the packet but we will not detect the duplication after :( DEBFILE("no more freeackret\n"); goodpacket = false; } } } } } return goodpacket; }
static void SOCK_Get(void) { size_t i, n; int j; ssize_t c; mysockaddr_t fromaddress; socklen_t fromlen; for (n = 0; n < mysocketses; n++) { fromlen = (socklen_t)sizeof(fromaddress); c = recvfrom(mysockets[n], (char *)&doomcom->data, MAXPACKETLENGTH, 0, (void *)&fromaddress, &fromlen); if (c != ERRSOCKET) { // find remote node number for (j = 0; j <= MAXNETNODES; j++) //include LAN { if (SOCK_cmpaddr(&fromaddress, &clientaddress[j], 0)) { doomcom->remotenode = (INT16)j; // good packet from a game player doomcom->datalength = (INT16)c; nodesocket[j] = mysockets[n]; return; } } // not found // find a free slot cleanupnodes(); j = getfreenode(); if (j > 0) { M_Memcpy(&clientaddress[j], &fromaddress, fromlen); nodesocket[j] = mysockets[n]; DEBFILE(va("New node detected: node:%d address:%s\n", j, SOCK_GetNodeAddress(j))); doomcom->remotenode = (INT16)j; // good packet from a game player doomcom->datalength = (INT16)c; // check if it's a banned dude so we can send a refusal later for (i = 0; i < numbans; i++) { if (SOCK_cmpaddr(&fromaddress, &banned[i], bannedmask[i])) { SOCK_bannednode[j] = true; DEBFILE("This dude has been banned\n"); break; } } if (i == numbans) SOCK_bannednode[j] = false; return; } else DEBFILE("New node detected: No more free slots\n"); } } doomcom->remotenode = -1; // no packet }