📄 d_net.c
字号:
// Emacs style mode select -*- C++ -*- //-----------------------------------------------------------------------------//// $Id: d_net.c,v 1.15 2001/02/10 12:27:13 bpereira Exp $//// Copyright (C) 1993-1996 by id Software, Inc.// Portions Copyright (C) 1998-2000 by DooM Legacy Team.//// This program is free software; you can redistribute it and/or// modify it under the terms of the GNU General Public License// as published by the Free Software Foundation; either version 2// of the License, or (at your option) any later version.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.////// $Log: d_net.c,v $// Revision 1.15 2001/02/10 12:27:13 bpereira// no message//// Revision 1.14 2000/10/21 08:43:28 bpereira// no message//// Revision 1.13 2000/10/16 20:02:29 bpereira// no message//// Revision 1.12 2000/10/08 13:29:59 bpereira// no message//// Revision 1.11 2000/09/28 20:57:14 bpereira// no message//// Revision 1.10 2000/09/15 19:49:21 bpereira// no message//// Revision 1.9 2000/09/10 10:38:18 metzgermeister// *** empty log message ***//// Revision 1.8 2000/09/01 19:34:37 bpereira// no message//// Revision 1.7 2000/09/01 18:23:42 hurdler// fix some issues with latest network code changes//// Revision 1.6 2000/08/31 14:30:55 bpereira// no message//// Revision 1.5 2000/04/16 18:38:07 bpereira// no message//// Revision 1.4 2000/03/29 19:39:48 bpereira// no message//// Revision 1.3 2000/02/27 00:42:10 hurdler// fix CR+LF problem//// Revision 1.2 2000/02/26 00:28:42 hurdler// Mostly bug fix (see borislog.txt 23-2-2000, 24-2-2000)////// DESCRIPTION:// DOOM Network game communication and protocol,// all OS independend parts.//// Implemente a Sliding window protocol without receiver window// (out of order reception)// This protocol use mix of "goback n" and "selective repeat" implementation// The NOTHING packet is send when connection is idle for acknowledge packets////-----------------------------------------------------------------------------#include "doomdef.h"#include "g_game.h"#include "i_net.h"#include "i_system.h"#include "m_argv.h"#include "d_net.h"#include "w_wad.h"#include "d_netfil.h"#include "d_clisrv.h"#include "z_zone.h"#include "i_tcp.h"//// NETWORKING//// gametic is the tic about to (or currently being) run// server:// maketic is the tic that hasn't had control made for it yet// nettics : is the tic for eatch node// firsttictosend : is the lowest value of nettics// client:// neededtic : is the tic needed by the client for run the game// firsttictosend : is used to optimize a condition// normaly maketic>=gametic>0,#define FORCECLOSE 0x8000#define CONNECTIONTIMEOUT (15*TICRATE)doomcom_t* doomcom;doomdata_t* netbuffer; // points inside doomcomFILE* debugfile=NULL; // put some net info in a file // during the game#define MAXREBOUND 8static doomdata_t reboundstore[MAXREBOUND];static short reboundsize[MAXREBOUND];static int rebound_head,rebound_tail;int net_bandwidth;short hardware_MAXPACKETLENGTH;void (*I_NetGet) (void);void (*I_NetSend) (void);boolean (*I_NetCanSend) (void);void (*I_NetCloseSocket) (void);void (*I_NetFreeNodenum) (int nodenum);int (*I_NetMakeNode) (char *address);boolean (*I_NetOpenSocket) (void);// network statstic_t statstarttic;int getbytes=0;INT64 sendbytes=0;int retransmit=0 ,duppacket=0;int sendackpacket=0,getackpacket=0;int ticruned=0 ,ticmiss=0;// globalsint getbps,sendbps;float lostpercent,duppercent,gamelostpercent;int packetheaderlength;boolean Net_GetNetStat(void){ tic_t t=I_GetTime();static int oldsendbyte=0; if( statstarttic+STATLENGTH<=t ) { getbps=(getbytes*TICRATE)/(t-statstarttic); sendbps=((sendbytes-oldsendbyte)*TICRATE)/(t-statstarttic); if(sendackpacket) lostpercent=100.0*(float)retransmit/(float)sendackpacket; else lostpercent=0; if(getackpacket) duppercent=100.0*(float)duppacket/(float)getackpacket; else duppercent=0; if( ticruned ) gamelostpercent=100.0*(float)ticmiss/(float)ticruned; else gamelostpercent=0; ticmiss=ticruned=0; oldsendbyte=sendbytes; getbytes=0; sendackpacket=getackpacket=duppacket=retransmit=0; statstarttic=t; return 1; } return 0;}// -----------------------------------------------------------------// Some stuct and function for acknowledgment of packets// -----------------------------------------------------------------#define MAXACKPACKETS 64 // minimum number of nodes#define MAXACKTOSEND 64#define URGENTFREESLOTENUM 6#define ACKTOSENDTIMEOUT (TICRATE/17)typedef struct { byte acknum; byte nextacknum; byte destinationnode; tic_t senttime; USHORT length; USHORT resentnum; char pak[MAXPACKETLENGTH];} ackpak_t;typedef enum { CLOSE = 1, // flag is set when connection is closing} node_flags_t;// table of packet that was not acknowleged can be resend (the sender window)static ackpak_t ackpak[MAXACKPACKETS];typedef struct { // ack return to send (like slinding window protocol) byte firstacktosend; // when no consecutive packet are received we keep in mind what packet // we already received in a queu byte acktosend_head; byte acktosend_tail; byte acktosend[MAXACKTOSEND]; // automaticaly send keep alive packet when not enought trafic tic_t lasttimeacktosend_sent; // detect connection lost tic_t lasttimepacketreceived; // flow control : do not sent to mush packet with ack byte remotefirstack; byte nextacknum; byte flags;// jacobson tcp timeout evaluation algorithm (Karn variation) fixed_t ping; fixed_t varping; int timeout; // computed with ping and varping} node_t;static node_t nodes[MAXNETNODES];#define PINGDEFAULT ((200*TICRATE*FRACUNIT)/1000)#define VARPINGDEFAULT ( (50*TICRATE*FRACUNIT)/1000)#define TIMEOUT(p,v) (p+4*v+FRACUNIT/2)>>FRACBITS;// return <0 if a<b (mod 256)// 0 if a=n (mod 256)// >0 if a>b (mod 256)// mnemonic: to use it compare to 0 : cmpack(a,b)<0 is "a<b" ...static int cmpack(byte a,byte b){ register int d=a-b; if(d>=127 || d<-128) return -d; return d;}// return a free acknum and copy netbuffer in the ackpak tablestatic boolean GetFreeAcknum(byte *freeack, boolean lowtimer){ node_t *node=&nodes[doomcom->remotenode]; int i,numfreeslote=0; if(cmpack((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==0) { // 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==0 ) node->nextacknum++; ackpak[i].destinationnode=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; } memcpy(ackpak[i].pak,netbuffer,ackpak[i].length); *freeack=ackpak[i].acknum; sendackpacket++; // for stat return true; }#ifdef PARANOIA if( devparm ) CONS_Printf("No more free ackpacket\n");#endif if( netbuffer->packettype < PT_CANFAIL ) I_Error("Connection lost\n"); return false;}// Get a ack to send in the queu of this nodestatic byte GetAcktosend(int node){ nodes[node].lasttimeacktosend_sent = I_GetTime(); return nodes[node].firstacktosend;}static void Removeack(int i){ int node=ackpak[i].destinationnode; 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,FIXED_TO_FLOAT(nodes[node].ping),FIXED_TO_FLOAT(nodes[node].varping),nodes[node].timeout)); ackpak[i].acknum=0; if( nodes[node].flags & CLOSE ) Net_CloseConnection( node );}// we have got a packet proceed the ack request and ack returnstatic boolean inline Processackpak(){ int i; boolean goodpacket=true; node_t *node=&nodes[doomcom->remotenode];// received a ack return 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 put it in to queue for send the ack back if( netbuffer->ack ) { byte ack=netbuffer->ack; getackpacket++; if( cmpack(ack,node->firstacktosend)<=0 ) { DEBFILE(va("Discard(1) ack %d (duplicated)\n",ack)); duppacket++; goodpacket=false; // discard packet (duplicat) } else { // check if it is not allready 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 (duplicat) break; } if( goodpacket ) { // is a good packet so increment the acknoledge number,then search a "hole" in the queue byte nextfirstack=node->firstacktosend+1; if(nextfirstack==0) nextfirstack=1; if(ack==nextfirstack) { byte hm1; // head-1 boolean change=true; node->firstacktosend=nextfirstack++; if(nextfirstack==0) nextfirstack=1; hm1=(node->acktosend_head-1+MAXACKTOSEND)%MAXACKTOSEND; while(change) { change=false; for( i=node->acktosend_tail;i!=node->acktosend_head;i=(i+1)%MAXACKTOSEND)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -