in doom_py/src/vizdoom/src/d_net.cpp [941:1380]
void NetUpdate (void)
{
//VIZDOOM_CODE
int lowtic;
int nowtime;
int newtics;
int i,j;
int realstart;
BYTE *cmddata;
bool resendOnly;
GC::CheckGC();
//VIZDOOM_CODE
if(*vizdoom_controlled && !*vizdoom_async) nowtime = gametic;
else{
if (ticdup == 0)
{
return;
}
//check time
nowtime = I_GetTime (false);
}
newtics = nowtime - gametime;
gametime = nowtime;
if (newtics <= 0) // nothing new to update
{
GetPackets ();
return;
}
if (skiptics <= newtics)
{
newtics -= skiptics;
skiptics = 0;
}
else
{
skiptics -= newtics;
newtics = 0;
}
// build new ticcmds for console player
for (i = 0; i < newtics; i++)
{
I_StartTic ();
//VIZDOOM_CODE
if(!*vizdoom_controlled || (*vizdoom_async && *vizdoom_allow_input)) D_ProcessEvents ();
if ((maketic - gametic) / ticdup >= BACKUPTICS/2-1) break; // can't hold any more
//Printf ("mk:%i ",maketic);
if(!*vizdoom_controlled || *vizdoom_async){
G_BuildTiccmd (&localcmds[maketic % LOCALCMDTICS]);
maketic++;
}
if (ticdup == 1 || maketic == 0)
{
Net_NewMakeTic ();
}
else
{
// Once ticdup tics have been collected, average their movements
// and combine their buttons, since they will all be sent as a
// single tic that gets duplicated ticdup times. Even with ticdup,
// tics are still collected at the normal rate so that, with the
// help of prediction, the game seems as responsive as normal.
if (maketic % ticdup != 0)
{
int mod = maketic - maketic % ticdup;
int j;
// Update the buttons for all tics in this ticdup set as soon as
// possible so that the prediction shows jumping as correctly as
// possible. (If you press +jump in the middle of a ticdup set,
// the jump will actually begin at the beginning of the set, not
// in the middle.)
for (j = maketic-2; j >= mod; --j)
{
localcmds[j % LOCALCMDTICS].ucmd.buttons |=
localcmds[(j + 1) % LOCALCMDTICS].ucmd.buttons;
}
}
else
{
// Average the ticcmds between these tics to get the
// movement that is actually sent across the network. We
// need to update them in all the localcmds slots that
// are dupped so that prediction works properly.
int mod = maketic - ticdup;
int modp, j;
int pitch = 0;
int yaw = 0;
int roll = 0;
int forwardmove = 0;
int sidemove = 0;
int upmove = 0;
for (j = 0; j < ticdup; ++j)
{
modp = (mod + j) % LOCALCMDTICS;
pitch += localcmds[modp].ucmd.pitch;
yaw += localcmds[modp].ucmd.yaw;
roll += localcmds[modp].ucmd.roll;
forwardmove += localcmds[modp].ucmd.forwardmove;
sidemove += localcmds[modp].ucmd.sidemove;
upmove += localcmds[modp].ucmd.upmove;
}
pitch /= ticdup;
yaw /= ticdup;
roll /= ticdup;
forwardmove /= ticdup;
sidemove /= ticdup;
upmove /= ticdup;
for (j = 0; j < ticdup; ++j)
{
modp = (mod + j) % LOCALCMDTICS;
localcmds[modp].ucmd.pitch = pitch;
localcmds[modp].ucmd.yaw = yaw;
localcmds[modp].ucmd.roll = roll;
localcmds[modp].ucmd.forwardmove = forwardmove;
localcmds[modp].ucmd.sidemove = sidemove;
localcmds[modp].ucmd.upmove = upmove;
}
Net_NewMakeTic ();
}
}
}
if (singletics)
return; // singletic update is synchronous
if (demoplayback)
{
resendto[0] = nettics[0] = (maketic / ticdup);
return; // Don't touch netcmd data while playing a demo, as it'll already exist.
}
// If maketic didn't cross a ticdup boundary, only send packets
// to players waiting for resends.
resendOnly = (maketic / ticdup) == (maketic - i) / ticdup;
// send the packet to the other nodes
int count = 1;
int quitcount = 0;
if (consoleplayer == Net_Arbitrator)
{
if (NetMode == NET_PacketServer)
{
for (j = 0; j < MAXPLAYERS; j++)
{
if (playeringame[j] && players[j].Bot == NULL)
{
count++;
}
}
// The loop above added the local player to the count a second time,
// and it also added the player being sent the packet to the count.
count -= 2;
for (j = 0; j < doomcom.numnodes; ++j)
{
if (nodejustleft[j])
{
if (count == 0)
{
PlayerIsGone (j, playerfornode[j]);
}
else
{
quitcount++;
}
}
}
if (count == 0)
{
count = 1;
}
}
}
for (i = 0; i < doomcom.numnodes; i++)
{
BYTE playerbytes[MAXPLAYERS];
if (!nodeingame[i])
{
continue;
}
if (NetMode == NET_PacketServer &&
consoleplayer != Net_Arbitrator &&
i != nodeforplayer[Net_Arbitrator] &&
i != 0)
{
continue;
}
if (resendOnly && resendcount[i] <= 0 && !remoteresend[i] && nettics[i])
{
continue;
}
int numtics;
int k;
lowtic = maketic / ticdup;
netbuffer[0] = 0;
netbuffer[1] = realstart = resendto[i];
k = 2;
if (NetMode == NET_PacketServer &&
consoleplayer == Net_Arbitrator &&
i != 0)
{
for (j = 1; j < doomcom.numnodes; ++j)
{
if (nodeingame[j] && nettics[j] < lowtic && j != i)
{
lowtic = nettics[j];
}
}
netbuffer[k++] = lowtic;
}
numtics = MAX(0, lowtic - realstart);
if (numtics > BACKUPTICS)
I_Error ("NetUpdate: Node %d missed too many tics", i);
switch (net_extratic)
{
case 0:
default:
resendto[i] = lowtic; break;
case 1: resendto[i] = MAX(0, lowtic - 1); break;
case 2: resendto[i] = nettics[i]; break;
}
if (numtics == 0 && resendOnly && !remoteresend[i] && nettics[i])
{
continue;
}
if (remoteresend[i])
{
netbuffer[0] |= NCMD_RETRANSMIT;
netbuffer[k++] = nettics[i];
}
if (numtics < 3)
{
netbuffer[0] |= numtics;
}
else
{
netbuffer[0] |= NCMD_XTICS;
netbuffer[k++] = numtics - 3;
}
if (quitcount > 0)
{
netbuffer[0] |= NCMD_QUITTERS;
netbuffer[k++] = quitcount;
for (int l = 0; l < doomcom.numnodes; ++l)
{
if (nodejustleft[l])
{
netbuffer[k++] = playerfornode[l];
}
}
}
// Send current network delay
// The number of tics we just made should be removed from the count.
netbuffer[k++] = ((maketic - numtics - gametic) / ticdup);
if (numtics > 0)
{
int l;
if (count > 1 && i != 0 && consoleplayer == Net_Arbitrator)
{
netbuffer[0] |= NCMD_MULTI;
netbuffer[k++] = count;
if (NetMode == NET_PacketServer)
{
for (l = 1, j = 0; j < MAXPLAYERS; j++)
{
if (playeringame[j] && players[j].Bot == NULL && j != playerfornode[i] && j != consoleplayer)
{
playerbytes[l++] = j;
netbuffer[k++] = j;
}
}
}
}
cmddata = &netbuffer[k];
for (l = 0; l < count; ++l)
{
for (j = 0; j < numtics; j++)
{
int start = realstart + j, prev = start - 1;
int localstart, localprev;
localstart = (start * ticdup) % LOCALCMDTICS;
localprev = (prev * ticdup) % LOCALCMDTICS;
start %= BACKUPTICS;
prev %= BACKUPTICS;
// The local player has their tics sent first, followed by
// the other players.
if (l == 0)
{
WriteWord (localcmds[localstart].consistancy, &cmddata);
// [RH] Write out special "ticcmds" before real ticcmd
if (specials.used[start])
{
memcpy (cmddata, specials.streams[start], specials.used[start]);
cmddata += specials.used[start];
}
WriteUserCmdMessage (&localcmds[localstart].ucmd,
localprev >= 0 ? &localcmds[localprev].ucmd : NULL, &cmddata);
}
else if (i != 0)
{
int len;
BYTE *spec;
WriteWord (netcmds[playerbytes[l]][start].consistancy, &cmddata);
spec = NetSpecs[playerbytes[l]][start].GetData (&len);
if (spec != NULL)
{
memcpy (cmddata, spec, len);
cmddata += len;
}
WriteUserCmdMessage (&netcmds[playerbytes[l]][start].ucmd,
prev >= 0 ? &netcmds[playerbytes[l]][prev].ucmd : NULL, &cmddata);
}
}
}
HSendPacket (i, int(cmddata - netbuffer));
}
else
{
HSendPacket (i, k);
}
}
// listen for other packets
GetPackets ();
if (!resendOnly)
{
// ideally nettics[0] should be 1 - 3 tics above lowtic
// if we are consistantly slower, speed up time
// [RH] I had erroneously assumed frameskip[] had 4 entries
// because there were 4 players, but that's not the case at
// all. The game is comparing the lag behind the master for
// four runs of TryRunTics. If our tic count is ahead of the
// master all 4 times, the next run of NetUpdate will not
// process any new input. If we have less input than the
// master, the next run of NetUpdate will process extra tics
// (because gametime gets decremented here).
// the key player does not adapt
if (consoleplayer != Net_Arbitrator)
{
// I'm not sure about this when using a packet server, because
// if left unmodified from the P2P version, it can make the game
// very jerky. The way I have it written right now basically means
// that it won't adapt. Fortunately, player prediction helps
// alleviate the lag somewhat.
if (NetMode == NET_PeerToPeer)
{
int totalavg = 0;
if (net_ticbalance)
{
// Try to guess ahead the time it takes to send responses to the slowest node
int nodeavg = 0, arbavg = 0;
for (j = 0; j < BACKUPTICS; j++)
{
arbavg += netdelay[nodeforplayer[Net_Arbitrator]][j];
nodeavg += netdelay[0][j];
}
arbavg /= BACKUPTICS;
nodeavg /= BACKUPTICS;
// We shouldn't adapt if we are already the arbitrator isn't what we are waiting for, otherwise it just adds more latency
if (arbavg > nodeavg)
{
lastaverage = totalavg = ((arbavg + nodeavg) / 2);
}
else
{
// Allow room to guess two tics ahead
if (nodeavg > (arbavg + 2) && lastaverage > 0)
lastaverage--;
totalavg = lastaverage;
}
}
mastertics = nettics[nodeforplayer[Net_Arbitrator]] + totalavg;
}
if (nettics[0] <= mastertics)
{
gametime--;
if (debugfile) fprintf(debugfile, "-");
}
if (NetMode != NET_PacketServer)
{
frameskip[(maketic / ticdup) & 3] = (oldnettics > mastertics);
}
else
{
frameskip[(maketic / ticdup) & 3] = (oldnettics - mastertics) > 3;
}
if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
{
skiptics = 1;
if (debugfile) fprintf(debugfile, "+");
}
oldnettics = nettics[0];
}
}
}