raze-gles/source/blood/src/network.cpp
Christoph Oelckers 6c5d9c3507 - moved the net init code out of the game frontends to avoid having to call atexit for their deinit function.
When doing this during startup it can be done by regular cleanup measures.
This also moves two larger chunks of networking code out of game.cpp.

Nevertheless, the fact that enet is a very dirty library which directly depends on Windows types is a big problem because it bleeds Windows definitions everywhere thanks to poor abstraction in all relevant layers.
2019-11-01 07:26:49 +01:00

1440 lines
47 KiB
C++

//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT
This file is part of NBlood.
NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
#include "ns.h" // Must come before everything else!
#include "build.h"
#include "mmulti.h"
#include "pragmas.h"
#ifndef NETCODE_DISABLE
#include "enet.h"
#endif
#include "compat.h"
#include "config.h"
#include "controls.h"
#include "globals.h"
#include "network.h"
#include "menu.h"
#include "player.h"
#include "seq.h"
#include "sound.h"
#include "view.h"
extern bool gHaveNetworking;
BEGIN_BLD_NS
char packet[576];
bool gStartNewGame = 0;
PACKETMODE gPacketMode = PACKETMODE_1;
ClockTicks gNetFifoClock = 0;
int gNetFifoTail = 0;
int gNetFifoHead[8];
int gPredictTail = 0;
int gNetFifoMasterTail = 0;
GINPUT gFifoInput[256][8];
int myMinLag[8];
int otherMinLag = 0;
int myMaxLag = 0;
unsigned int gChecksum[4];
unsigned int gCheckFifo[256][8][4];
int gCheckHead[8];
int gSendCheckTail = 0;
int gCheckTail = 0;
int gInitialNetPlayers = 0;
int gBufferJitter = 1;
int gPlayerReady[8];
int gSyncRate = 1;
bool bNoResend = true;
bool gRobust = false;
bool bOutOfSync = false;
bool ready2send = false;
NETWORKMODE gNetMode = NETWORK_NONE;
char gNetAddress[32];
// PORT-TODO: Use different port?
int gNetPort = kNetDefaultPort;
const short word_1328AC = 0x214;
struct struct28E3B0
{
int at0;
int at4;
int at8;
int atc;
int at10;
int at14;
int at18;
char at1c;
int at1d;
};
struct28E3B0 byte_28E3B0;
PKT_STARTGAME gPacketStartGame;
#ifndef NETCODE_DISABLE
ENetAddress gNetENetAddress;
ENetHost *gNetENetServer;
ENetHost *gNetENetClient;
ENetPeer *gNetENetPeer;
ENetPeer *gNetPlayerPeer[kMaxPlayers];
bool gNetENetInit = false;
#define kENetFifoSize 2048
//ENetPacket *gNetServerPacketFifo[kENetFifoSize];
//int gNetServerPacketHead, gNetServerPacketTail;
ENetPacket *gNetPacketFifo[kENetFifoSize];
int gNetPacketHead, gNetPacketTail;
enum BLOOD_ENET_CHANNEL {
BLOOD_ENET_SERVICE = 0,
BLOOD_ENET_GAME,
BLOOD_ENET_CHANNEL_MAX
};
enum BLOOD_SERVICE {
BLOOD_SERVICE_CONNECTINFO = 0,
BLOOD_SERVICE_CONNECTID,
BLOOD_SERVICE_SENDTOID,
};
struct PKT_CONNECTINFO {
short numplayers;
short connecthead;
short connectpoint2[kMaxPlayers];
};
void netServerDisconnect(void)
{
ENetEvent event;
if (gNetMode != NETWORK_SERVER)
return;
for (int p = 0; p < kMaxPlayers; p++)
if (gNetPlayerPeer[p])
{
bool bDisconnectStatus = false;
enet_peer_disconnect_later(gNetPlayerPeer[p], 0);
if (enet_host_service(gNetENetServer, &event, 3000) > 0)
{
switch (event.type)
{
case ENET_EVENT_TYPE_DISCONNECT:
bDisconnectStatus = true;
break;
default:
break;
}
}
if (!bDisconnectStatus)
enet_peer_reset(gNetPlayerPeer[p]);
gNetPlayerPeer[p] = NULL;
}
}
void netClientDisconnect(void)
{
ENetEvent event;
if (gNetMode != NETWORK_CLIENT || gNetENetPeer == NULL)
return;
enet_peer_disconnect_later(gNetENetPeer, 0);
bool bDisconnectStatus = false;
if (enet_host_service(gNetENetClient, &event, 3000) > 0)
{
switch (event.type)
{
case ENET_EVENT_TYPE_DISCONNECT:
bDisconnectStatus = true;
break;
default:
break;
}
}
if (!bDisconnectStatus)
enet_peer_reset(gNetENetPeer);
gNetENetPeer = NULL;
}
#endif
void netResetToSinglePlayer(void)
{
myconnectindex = connecthead = 0;
gInitialNetPlayers = gNetPlayers = numplayers = 1;
connectpoint2[0] = -1;
gGameOptions.nGameType = 0;
gNetMode = NETWORK_NONE;
UpdateNetworkMenus();
}
void netSendPacket(int nDest, char *pBuffer, int nSize)
{
#ifndef NETCODE_DISABLE
if (gNetMode == NETWORK_NONE)
return;
netUpdate();
if (gNetMode == NETWORK_SERVER)
{
if (gNetPlayerPeer[nDest] != NULL)
{
ENetPacket *pNetPacket = enet_packet_create(NULL, nSize + 1, ENET_PACKET_FLAG_RELIABLE);
char *pPBuffer = (char*)pNetPacket->data;
PutPacketByte(pPBuffer, myconnectindex);
PutPacketBuffer(pPBuffer, pBuffer, nSize);
enet_peer_send(gNetPlayerPeer[nDest], BLOOD_ENET_GAME, pNetPacket);
enet_host_service(gNetENetServer, NULL, 0);
}
}
else
{
if (nDest == 0)
{
ENetPacket *pNetPacket = enet_packet_create(NULL, nSize + 1, ENET_PACKET_FLAG_RELIABLE);
char *pPBuffer = (char*)pNetPacket->data;
PutPacketByte(pPBuffer, myconnectindex);
PutPacketBuffer(pPBuffer, pBuffer, nSize);
enet_peer_send(gNetENetPeer, BLOOD_ENET_GAME, pNetPacket);
}
else
{
ENetPacket *pNetPacket = enet_packet_create(NULL, nSize + 3, ENET_PACKET_FLAG_RELIABLE);
char *pPBuffer = (char*)pNetPacket->data;
PutPacketByte(pPBuffer, BLOOD_SERVICE_SENDTOID);
PutPacketByte(pPBuffer, nDest);
PutPacketByte(pPBuffer, myconnectindex);
PutPacketBuffer(pPBuffer, pBuffer, nSize);
enet_peer_send(gNetENetPeer, BLOOD_ENET_SERVICE, pNetPacket);
}
enet_host_service(gNetENetClient, NULL, 0);
}
netUpdate();
#endif
}
void netSendPacketAll(char *pBuffer, int nSize)
{
for (int p = connecthead; p >= 0; p = connectpoint2[p])
if (p != myconnectindex)
netSendPacket(p, pBuffer, nSize);
}
void sub_79760(void)
{
gNetFifoClock = gFrameClock = totalclock = 0;
gNetFifoMasterTail = 0;
gPredictTail = 0;
gNetFifoTail = 0;
memset(gNetFifoHead, 0, sizeof(gNetFifoHead));
memset(gCheckFifo, 0, sizeof(gCheckFifo));
memset(myMinLag, 0, sizeof(myMinLag));
otherMinLag = 0;
myMaxLag = 0;
memset(gCheckHead, 0, sizeof(gCheckHead));
gSendCheckTail = 0;
gCheckTail = 0;
memset(&byte_28E3B0, 0, sizeof(byte_28E3B0));
bOutOfSync = 0;
gBufferJitter = 1;
}
void CalcGameChecksum(void)
{
memset(gChecksum, 0, sizeof(gChecksum));
gChecksum[0] = wrand();
for (int p = connecthead; p >= 0; p = connectpoint2[p])
{
int *pBuffer = &gPlayer[p].used1;
int sum = 0;
int length = ((char*)&gPlayer[p+1]-(char*)pBuffer)/4;
while (length--)
{
sum += *pBuffer++;
}
gChecksum[1] ^= sum;
pBuffer = (int*)gPlayer[p].pSprite;
sum = 0;
length = sizeof(spritetype)/4;
while (length--)
{
sum += *pBuffer++;
}
gChecksum[2] ^= sum;
pBuffer = (int*)gPlayer[p].pXSprite;
sum = 0;
length = sizeof(XSPRITE)/4;
while (length--)
{
sum += *pBuffer++;
}
gChecksum[3] ^= sum;
}
}
void netCheckSync(void)
{
char buffer[80];
if (gGameOptions.nGameType == 0)
return;
if (numplayers == 1)
return;
if (bOutOfSync)
return;
while (1)
{
for (int p = connecthead; p >= 0; p = connectpoint2[p])
{
if (gCheckTail >= gCheckHead[p])
return;
}
for (int p = connecthead; p >= 0; p = connectpoint2[p])
{
if (p != myconnectindex)
{
int status = memcmp(gCheckFifo[gCheckTail&255][p], gCheckFifo[gCheckTail&255][connecthead], 16);
if (status)
{
sprintf(buffer, "OUT OF SYNC (%d)", p);
char *pBuffer = buffer + strlen(buffer);
for (unsigned int i = 0; i < 4; i++)
{
if (gCheckFifo[gCheckTail&255][p][i] != gCheckFifo[gCheckTail&255][connecthead][i])
pBuffer += sprintf(pBuffer, " %d", i);
}
viewSetErrorMessage(buffer);
bOutOfSync = 1;
}
}
}
gCheckTail++;
}
}
short netGetPacket(short *pSource, char *pMessage)
{
#ifndef NETCODE_DISABLE
if (gNetMode == NETWORK_NONE)
return 0;
netUpdate();
if (gNetPacketTail != gNetPacketHead)
{
ENetPacket *pEPacket = gNetPacketFifo[gNetPacketTail];
gNetPacketTail = (gNetPacketTail+1)%kENetFifoSize;
char *pPacket = (char*)pEPacket->data;
*pSource = GetPacketByte(pPacket);
int nSize = pEPacket->dataLength-1;
memcpy(pMessage, pPacket, nSize);
enet_packet_destroy(pEPacket);
netUpdate();
return nSize;
}
netUpdate();
#endif
return 0;
}
void netGetPackets(void)
{
short nPlayer;
short nSize;
char buffer[128];
while ((nSize = netGetPacket(&nPlayer, packet)) > 0)
{
char *pPacket = packet;
switch (GetPacketByte(pPacket))
{
case 0: // From master
for (int p = connecthead; p >= 0; p = connectpoint2[p])
{
if (p != myconnectindex)
{
GINPUT *pInput = &gFifoInput[gNetFifoHead[p]&255][p];
memset(pInput, 0, sizeof(GINPUT));
pInput->syncFlags.byte = GetPacketByte(pPacket);
pInput->forward = GetPacketWord(pPacket);
pInput->q16turn = GetPacketDWord(pPacket);
pInput->strafe = GetPacketWord(pPacket);
if (pInput->syncFlags.buttonChange)
pInput->buttonFlags.byte = GetPacketByte(pPacket);
if (pInput->syncFlags.keyChange)
pInput->keyFlags.word = GetPacketWord(pPacket);
if (pInput->syncFlags.useChange)
pInput->useFlags.byte = GetPacketByte(pPacket);
if (pInput->syncFlags.weaponChange)
pInput->newWeapon = GetPacketByte(pPacket);
if (pInput->syncFlags.mlookChange)
pInput->q16mlook = GetPacketDWord(pPacket);
gNetFifoHead[p]++;
}
else
{
SYNCFLAGS syncFlags;
syncFlags.byte = GetPacketByte(pPacket);
pPacket += 2+4+2;
if (syncFlags.buttonChange)
pPacket++;
if (syncFlags.keyChange)
pPacket+=2;
if (syncFlags.useChange)
pPacket++;
if (syncFlags.weaponChange)
pPacket++;
if (syncFlags.mlookChange)
pPacket+=4;
}
}
if (((gNetFifoHead[connecthead]-1)&15)==0)
{
for (int p = connectpoint2[connecthead]; p >= 0; p = connectpoint2[p])
{
int nLag = (signed char)GetPacketByte(pPacket);
if (p == myconnectindex)
otherMinLag = nLag;
}
}
while (pPacket < packet+nSize)
{
int checkSum[4];
GetPacketBuffer(pPacket, checkSum, sizeof(checkSum));
for (int p = connecthead; p >= 0; p = connectpoint2[p])
{
if (p != myconnectindex)
{
memcpy(gCheckFifo[gCheckHead[p]&255][p], checkSum, sizeof(checkSum));
gCheckHead[p]++;
}
}
}
break;
case 1: // From slave
{
GINPUT *pInput = &gFifoInput[gNetFifoHead[nPlayer]&255][nPlayer];
memset(pInput, 0, sizeof(GINPUT));
pInput->syncFlags.byte = GetPacketByte(pPacket);
pInput->forward = GetPacketWord(pPacket);
pInput->q16turn = GetPacketDWord(pPacket);
pInput->strafe = GetPacketWord(pPacket);
if (pInput->syncFlags.buttonChange)
pInput->buttonFlags.byte = GetPacketByte(pPacket);
if (pInput->syncFlags.keyChange)
pInput->keyFlags.word = GetPacketWord(pPacket);
if (pInput->syncFlags.useChange)
pInput->useFlags.byte = GetPacketByte(pPacket);
if (pInput->syncFlags.weaponChange)
pInput->newWeapon = GetPacketByte(pPacket);
if (pInput->syncFlags.mlookChange)
pInput->q16mlook = GetPacketDWord(pPacket);
gNetFifoHead[nPlayer]++;
while (pPacket < packet+nSize)
{
int checkSum[4];
GetPacketBuffer(pPacket, checkSum, sizeof(checkSum));
memcpy(gCheckFifo[gCheckHead[nPlayer]&255][nPlayer], checkSum, sizeof(checkSum));
gCheckHead[nPlayer]++;
}
break;
}
case 2:
{
if (nPlayer == connecthead && (gNetFifoHead[nPlayer]&15) == 0)
{
for (int p = connectpoint2[connecthead]; p >= 0; p = connectpoint2[p])
{
int nLag = (signed char)GetPacketByte(pPacket);
if (p == myconnectindex)
otherMinLag = nLag;
}
}
GINPUT *pInput = &gFifoInput[gNetFifoHead[nPlayer]&255][nPlayer];
memset(pInput, 0, sizeof(GINPUT));
pInput->syncFlags.byte = GetPacketByte(pPacket);
pInput->forward = GetPacketWord(pPacket);
pInput->q16turn = GetPacketDWord(pPacket);
pInput->strafe = GetPacketWord(pPacket);
if (pInput->syncFlags.buttonChange)
pInput->buttonFlags.byte = GetPacketByte(pPacket);
if (pInput->syncFlags.keyChange)
pInput->keyFlags.word = GetPacketWord(pPacket);
if (pInput->syncFlags.useChange)
pInput->useFlags.byte = GetPacketByte(pPacket);
if (pInput->syncFlags.weaponChange)
pInput->newWeapon = GetPacketByte(pPacket);
if (pInput->syncFlags.mlookChange)
pInput->q16mlook = GetPacketDWord(pPacket);
gNetFifoHead[nPlayer]++;
for (int i = gSyncRate; i > 1; i--)
{
GINPUT *pInput2 = &gFifoInput[gNetFifoHead[nPlayer]&255][nPlayer];
memcpy(pInput2, pInput, sizeof(GINPUT));
pInput2->keyFlags.word = 0;
pInput2->useFlags.byte = 0;
pInput2->newWeapon = 0;
pInput2->syncFlags.weaponChange = 0;
gNetFifoHead[nPlayer]++;
}
while (pPacket < packet+nSize)
{
int checkSum[4];
GetPacketBuffer(pPacket, checkSum, sizeof(checkSum));
memcpy(gCheckFifo[gCheckHead[nPlayer]&255][nPlayer], checkSum, sizeof(checkSum));
gCheckHead[nPlayer]++;
}
break;
}
case 3:
pPacket += 4;
if (*pPacket != '/' || (*pPacket == 0 && *(pPacket+1) == 0) || (*(pPacket+1) >= '1' && *(pPacket+1) <= '8' && *(pPacket+1)-'1' == myconnectindex))
{
sprintf(buffer, VanillaMode() ? "%s : %s" : "%s: %s", gProfile[nPlayer].name, pPacket);
viewSetMessage(buffer, VanillaMode() ? 0 : 10); // 10: dark blue
sndStartSample("DMRADIO", 128, -1);
}
break;
case 4:
sndStartSample(4400+GetPacketByte(pPacket), 128, 1, 0);
break;
case 7:
nPlayer = GetPacketDWord(pPacket);
dassert(nPlayer != myconnectindex);
netWaitForEveryone(0);
netPlayerQuit(nPlayer);
netWaitForEveryone(0);
break;
case 249:
nPlayer = GetPacketDWord(pPacket);
dassert(nPlayer != myconnectindex);
netPlayerQuit(nPlayer);
netWaitForEveryone(0);
break;
case 250:
gPlayerReady[nPlayer]++;
break;
case 251:
memcpy(&gProfile[nPlayer], pPacket, sizeof(PROFILE));
break;
case 252:
pPacket += 4;
memcpy(&gPacketStartGame, pPacket, sizeof(PKT_STARTGAME));
if (gPacketStartGame.version != word_1328AC)
ThrowError("\nThese versions of Blood cannot play together.\n");
gStartNewGame = 1;
break;
case 255:
inputState.SetKeyStatus(sc_Escape, 1);
break;
}
}
}
void netBroadcastPlayerLogoff(int nPlayer)
{
if (numplayers < 2)
return;
netWaitForEveryone(0);
netPlayerQuit(nPlayer);
if (nPlayer != myconnectindex)
netWaitForEveryone(0);
}
void netBroadcastMyLogoff(bool bRestart)
{
if (numplayers < 2)
return;
char *pPacket = packet;
PutPacketByte(pPacket, 7);
PutPacketDWord(pPacket, myconnectindex);
netSendPacketAll(packet, pPacket - packet);
netWaitForEveryone(0);
ready2send = 0;
gQuitGame = true;
if (bRestart)
gRestartGame = true;
netDeinitialize();
netResetToSinglePlayer();
}
void netBroadcastPlayerInfo(int nPlayer)
{
PROFILE *pProfile = &gProfile[nPlayer];
strcpy(pProfile->name, playername);
pProfile->skill = gSkill;
pProfile->nAutoAim = cl_autoaim;
pProfile->nWeaponSwitch = cl_weaponswitch;
if (numplayers < 2)
return;
char *pPacket = packet;
PutPacketByte(pPacket, 251);
PutPacketBuffer(pPacket, pProfile, sizeof(PROFILE));
netSendPacketAll(packet, pPacket-packet);
}
void netBroadcastNewGame(void)
{
if (numplayers < 2)
return;
gPacketStartGame.version = word_1328AC;
char *pPacket = packet;
PutPacketByte(pPacket, 252);
PutPacketDWord(pPacket, myconnectindex);
PutPacketBuffer(pPacket, &gPacketStartGame, sizeof(PKT_STARTGAME));
netSendPacketAll(packet, pPacket-packet);
}
void netBroadcastTaunt(int nPlayer, int nTaunt)
{
UNREFERENCED_PARAMETER(nPlayer);
if (numplayers > 1)
{
char *pPacket = packet;
PutPacketByte(pPacket, 4);
PutPacketByte(pPacket, nTaunt);
netSendPacketAll(packet, pPacket-packet);
}
sndStartSample(4400+nTaunt, 128, 1, 0);
}
void netBroadcastMessage(int nPlayer, const char *pzMessage)
{
if (numplayers > 1)
{
int nSize = strlen(pzMessage);
char *pPacket = packet;
PutPacketByte(pPacket, 3);
PutPacketDWord(pPacket, nPlayer);
PutPacketBuffer(pPacket, pzMessage, nSize+1);
netSendPacketAll(packet, pPacket-packet);
}
}
void netWaitForEveryone(char a1)
{
if (numplayers < 2)
return;
char *pPacket = packet;
PutPacketByte(pPacket, 250);
netSendPacketAll(packet, pPacket-packet);
gPlayerReady[myconnectindex]++;
int p;
do
{
if (inputState.EscapePressed() && a1)
Bexit(0);
gameHandleEvents();
faketimerhandler();
for (p = connecthead; p >= 0; p = connectpoint2[p])
if (gPlayerReady[p] < gPlayerReady[myconnectindex])
break;
if (gRestartGame || gNetPlayers <= 1)
break;
} while (p >= 0);
}
void sub_7AC28(const char *pzString)
{
if (numplayers < 2)
return;
if (pzString)
{
int nLength = strlen(pzString);
if (nLength > 0)
{
char *pPacket = packet;
PutPacketByte(pPacket, 5);
PutPacketBuffer(pPacket, pzString, nLength+1);
netSendPacketAll(packet, pPacket-packet);
}
}
}
void netSendEmptyPackets(void)
{
ClockTicks nClock = totalclock;
char *pPacket = packet;
PutPacketByte(pPacket, 254);
for (int i = 0; i < 8; i++)
{
if (nClock <= totalclock)
{
nClock = totalclock+4;
netSendPacketAll(packet, pPacket-packet);
}
}
}
void sub_7AD90(GINPUT *pInput)
{
byte_28E3B0.at0 |= pInput->syncFlags.byte;
byte_28E3B0.at4 += pInput->forward;
byte_28E3B0.at8 += pInput->q16turn;
byte_28E3B0.atc += pInput->strafe;
byte_28E3B0.at10 |= pInput->buttonFlags.byte;
byte_28E3B0.at14 |= pInput->keyFlags.word;
byte_28E3B0.at18 |= pInput->useFlags.byte;
if (pInput->newWeapon)
byte_28E3B0.at1c = pInput->newWeapon;
byte_28E3B0.at1d = pInput->q16mlook;
}
void sub_7AE2C(GINPUT *pInput)
{
pInput->syncFlags.byte = byte_28E3B0.at0;
pInput->forward = byte_28E3B0.at4;
pInput->q16turn = byte_28E3B0.at8;
pInput->strafe = byte_28E3B0.atc;
pInput->buttonFlags.byte = byte_28E3B0.at10;
pInput->keyFlags.word = byte_28E3B0.at14;
pInput->useFlags.byte = byte_28E3B0.at18;
pInput->newWeapon = byte_28E3B0.at1c;
pInput->q16mlook = byte_28E3B0.at1d;
memset(&byte_28E3B0, 0, sizeof(byte_28E3B0));
}
void netMasterUpdate(void)
{
if (myconnectindex != connecthead)
return;
char v4 = 0;
do
{
for (int p = connecthead; p >= 0; p = connectpoint2[p])
if (gNetFifoMasterTail >= gNetFifoHead[p])
{
if (v4)
return;
char *pPacket = packet;
PutPacketByte(pPacket, 254);
for (; p >= 0; p = connectpoint2[p])
netSendPacket(p, packet, pPacket-packet);
return;
}
v4 = 1;
char *pPacket = packet;
PutPacketByte(pPacket, 0);
for (int p = connecthead; p >= 0; p = connectpoint2[p])
{
GINPUT *pInput = &gFifoInput[gNetFifoMasterTail&255][p];
if (pInput->buttonFlags.byte)
pInput->syncFlags.buttonChange = 1;
if (pInput->keyFlags.word)
pInput->syncFlags.keyChange = 1;
if (pInput->useFlags.byte)
pInput->syncFlags.useChange = 1;
if (pInput->newWeapon)
pInput->syncFlags.weaponChange = 1;
if (pInput->q16mlook)
pInput->syncFlags.mlookChange = 1;
PutPacketByte(pPacket, pInput->syncFlags.byte);
PutPacketWord(pPacket, pInput->forward);
PutPacketDWord(pPacket, pInput->q16turn);
PutPacketWord(pPacket, pInput->strafe);
if (pInput->syncFlags.buttonChange)
PutPacketByte(pPacket, pInput->buttonFlags.byte);
if (pInput->syncFlags.keyChange)
PutPacketWord(pPacket, pInput->keyFlags.word);
if (pInput->syncFlags.useChange)
PutPacketByte(pPacket, pInput->useFlags.byte);
if (pInput->syncFlags.weaponChange)
PutPacketByte(pPacket, pInput->newWeapon);
if (pInput->syncFlags.mlookChange)
PutPacketDWord(pPacket, pInput->q16mlook);
}
if ((gNetFifoMasterTail&15) == 0)
{
for (int p = connectpoint2[connecthead]; p >= 0; p = connectpoint2[p])
PutPacketByte(pPacket, ClipRange(myMinLag[p], -128, 127));
for (int p = connecthead; p >= 0; p = connectpoint2[p])
myMinLag[p] = 0x7fffffff;
}
while (gSendCheckTail != gCheckHead[myconnectindex])
{
PutPacketBuffer(pPacket, gCheckFifo[gSendCheckTail&255][myconnectindex], 16);
gSendCheckTail++;
}
for (int p = connectpoint2[connecthead]; p >= 0; p = connectpoint2[p])
netSendPacket(p, packet, pPacket-packet);
gNetFifoMasterTail++;
} while (1);
}
void netGetInput(void)
{
if (numplayers > 1)
netGetPackets();
for (int p = connecthead; p >= 0; p = connectpoint2[p])
if (gNetFifoHead[myconnectindex]-200 > gNetFifoHead[p])
return;
ctrlGetInput();
sub_7AD90(&gInput);
if (gNetFifoHead[myconnectindex]&(gSyncRate-1))
{
GINPUT *pInput1 = &gFifoInput[gNetFifoHead[myconnectindex]&255][myconnectindex];
GINPUT *pInput2 = &gFifoInput[(gNetFifoHead[myconnectindex]-1)&255][myconnectindex];
memcpy(pInput1, pInput2, sizeof(GINPUT));
pInput1->keyFlags.word = 0;
pInput1->useFlags.byte = 0;
pInput1->newWeapon = 0;
pInput1->syncFlags.weaponChange = 0;
gNetFifoHead[myconnectindex]++;
return;
}
GINPUT *pInput = &gFifoInput[gNetFifoHead[myconnectindex]&255][myconnectindex];
sub_7AE2C(pInput);
memcpy(&gInput, pInput, sizeof(GINPUT));
gNetFifoHead[myconnectindex]++;
if (gGameOptions.nGameType == 0 || numplayers == 1)
{
for (int p = connecthead; p >= 0; p = connectpoint2[p])
{
if (p != myconnectindex)
{
GINPUT *pInput1 = &gFifoInput[(gNetFifoHead[p]-1)&255][p];
GINPUT *pInput2 = &gFifoInput[gNetFifoHead[p]&255][p];
memcpy(pInput2, pInput1, sizeof(GINPUT));
gNetFifoHead[p]++;
}
}
return;
}
for (int p = connecthead; p >= 0; p = connectpoint2[p])
{
if (p != myconnectindex)
{
int nLag = gNetFifoHead[myconnectindex]-1-gNetFifoHead[p];
myMinLag[p] = ClipHigh(nLag, myMinLag[p]);
myMaxLag = ClipLow(nLag, myMaxLag);
}
}
if (((gNetFifoHead[myconnectindex]-1)&15) == 0)
{
int t = myMaxLag-gBufferJitter;
myMaxLag = 0;
if (t > 0)
gBufferJitter += (3+t)>>2;
else if (t < 0)
gBufferJitter -= (1-t)>>2;
}
if (gPacketMode == PACKETMODE_2)
{
char *pPacket = packet;
PutPacketByte(pPacket, 2);
if (((gNetFifoHead[myconnectindex]-1)&15) == 0)
{
if (myconnectindex == connecthead)
{
for (int p = connectpoint2[connecthead]; p >= 0; p = connectpoint2[p])
PutPacketByte(pPacket, ClipRange(myMinLag[p], -128, 127));
}
else
{
int t = myMinLag[connecthead]-otherMinLag;
if (klabs(t) > 2)
{
if (klabs(t) > 8)
{
if (t < 0)
t++;
t >>= 1;
}
else
t = ksgn(t);
totalclock -= t<<2;
otherMinLag += t;
myMinLag[connecthead] -= t;
}
}
for (int p = connecthead; p >= 0; p = connectpoint2[p])
myMinLag[p] = 0x7fffffff;
}
if (gInput.buttonFlags.byte)
gInput.syncFlags.buttonChange = 1;
if (gInput.keyFlags.word)
gInput.syncFlags.keyChange = 1;
if (gInput.useFlags.byte)
gInput.syncFlags.useChange = 1;
if (gInput.newWeapon)
gInput.syncFlags.weaponChange = 1;
if (gInput.q16mlook)
gInput.syncFlags.mlookChange = 1;
PutPacketByte(pPacket, gInput.syncFlags.byte);
PutPacketWord(pPacket, gInput.forward);
PutPacketDWord(pPacket, gInput.q16turn);
PutPacketWord(pPacket, gInput.strafe);
if (gInput.syncFlags.buttonChange)
PutPacketByte(pPacket, gInput.buttonFlags.byte);
if (gInput.syncFlags.keyChange)
PutPacketWord(pPacket, gInput.keyFlags.word);
if (gInput.syncFlags.useChange)
PutPacketByte(pPacket, gInput.useFlags.byte);
if (gInput.syncFlags.weaponChange)
PutPacketByte(pPacket, gInput.newWeapon);
if (gInput.syncFlags.mlookChange)
PutPacketDWord(pPacket, gInput.q16mlook);
while (gSendCheckTail != gCheckHead[myconnectindex])
{
unsigned int *checkSum = gCheckFifo[gSendCheckTail&255][myconnectindex];
PutPacketBuffer(pPacket, checkSum, 16);
gSendCheckTail++;
}
netSendPacketAll(packet, pPacket-packet);
return;
}
if (myconnectindex != connecthead)
{
char *pPacket = packet;
PutPacketByte(pPacket, 1);
if (gInput.buttonFlags.byte)
gInput.syncFlags.buttonChange = 1;
if (gInput.keyFlags.word)
gInput.syncFlags.keyChange = 1;
if (gInput.useFlags.byte)
gInput.syncFlags.useChange = 1;
if (gInput.newWeapon)
gInput.syncFlags.weaponChange = 1;
if (gInput.q16mlook)
gInput.syncFlags.mlookChange = 1;
PutPacketByte(pPacket, gInput.syncFlags.byte);
PutPacketWord(pPacket, gInput.forward);
PutPacketDWord(pPacket, gInput.q16turn);
PutPacketWord(pPacket, gInput.strafe);
if (gInput.syncFlags.buttonChange)
PutPacketByte(pPacket, gInput.buttonFlags.byte);
if (gInput.syncFlags.keyChange)
PutPacketWord(pPacket, gInput.keyFlags.word);
if (gInput.syncFlags.useChange)
PutPacketByte(pPacket, gInput.useFlags.byte);
if (gInput.syncFlags.weaponChange)
PutPacketByte(pPacket, gInput.newWeapon);
if (gInput.syncFlags.mlookChange)
PutPacketDWord(pPacket, gInput.q16mlook);
if (((gNetFifoHead[myconnectindex]-1)&15) == 0)
{
int t = myMinLag[connecthead]-otherMinLag;
if (klabs(t) > 2)
{
if (klabs(t) > 8)
{
if (t < 0)
t++;
t >>= 1;
}
else
t = ksgn(t);
totalclock -= t<<2;
otherMinLag += t;
myMinLag[connecthead] -= t;
}
for (int p = connecthead; p >= 0; p = connectpoint2[p])
myMinLag[p] = 0x7fffffff;
}
while (gSendCheckTail != gCheckHead[myconnectindex])
{
PutPacketBuffer(pPacket, gCheckFifo[gSendCheckTail&255][myconnectindex], 16);
gSendCheckTail++;
}
netSendPacket(connecthead, packet, pPacket-packet);
return;
}
netMasterUpdate();
}
void netInitialize(bool bConsole)
{
netDeinitialize();
memset(gPlayerReady, 0, sizeof(gPlayerReady));
sub_79760();
#ifndef NETCODE_DISABLE
char buffer[128];
gNetENetServer = gNetENetClient = NULL;
//gNetServerPacketHead = gNetServerPacketTail = 0;
gNetPacketHead = gNetPacketTail = 0;
if (gNetMode == NETWORK_NONE)
{
netResetToSinglePlayer();
return;
}
if (!gHaveNetworking)
{
initprintf("An error occurred while initializing ENet.\n");
netResetToSinglePlayer();
return;
}
if (gNetMode == NETWORK_SERVER)
{
memset(gNetPlayerPeer, 0, sizeof(gNetPlayerPeer));
ENetEvent event;
gNetENetAddress.host = ENET_HOST_ANY;
gNetENetAddress.port = gNetPort;
gNetENetServer = enet_host_create(&gNetENetAddress, 8, BLOOD_ENET_CHANNEL_MAX, 0, 0);
if (!gNetENetServer)
{
initprintf("An error occurred while trying to create an ENet server host.\n");
netDeinitialize();
netResetToSinglePlayer();
return;
}
numplayers = 1;
// Wait for clients
if (!bConsole)
{
char buffer[128];
sprintf(buffer, "Waiting for players (%i\\%i)", numplayers, gNetPlayers);
viewLoadingScreen(2518, "Network Game", NULL, buffer);
videoNextPage();
}
while (numplayers < gNetPlayers)
{
handleevents();
if (quitevent)
{
netServerDisconnect();
QuitGame();
}
if (!bConsole && KB_KeyPressed(sc_Escape))
{
netServerDisconnect();
netDeinitialize();
netResetToSinglePlayer();
return;
}
enet_host_service(gNetENetServer, NULL, 0);
if (enet_host_check_events(gNetENetServer, &event) > 0)
{
switch (event.type)
{
case ENET_EVENT_TYPE_CONNECT:
{
char ipaddr[32];
enet_address_get_host_ip(&event.peer->address, ipaddr, sizeof(ipaddr));
initprintf("Client connected: %s:%u\n", ipaddr, event.peer->address.port);
numplayers++;
for (int i = 1; i < kMaxPlayers; i++)
{
if (gNetPlayerPeer[i] == NULL)
{
gNetPlayerPeer[i] = event.peer;
break;
}
}
if (!bConsole)
{
char buffer[128];
sprintf(buffer, "Waiting for players (%i\\%i)", numplayers, gNetPlayers);
viewLoadingScreen(2518, "Network Game", NULL, buffer);
videoNextPage();
}
break;
}
case ENET_EVENT_TYPE_DISCONNECT:
{
char ipaddr[32];
enet_address_get_host_ip(&event.peer->address, ipaddr, sizeof(ipaddr));
initprintf("Client disconnected: %s:%u\n", ipaddr, event.peer->address.port);
numplayers--;
for (int i = 1; i < kMaxPlayers; i++)
{
if (gNetPlayerPeer[i] == event.peer)
{
gNetPlayerPeer[i] = NULL;
for (int j = kMaxPlayers-1; j > i; j--)
{
if (gNetPlayerPeer[j])
{
gNetPlayerPeer[i] = gNetPlayerPeer[j];
gNetPlayerPeer[j] = NULL;
break;
}
}
}
}
if (!bConsole)
{
char buffer[128];
sprintf(buffer, "Waiting for players (%i\\%i)", numplayers, gNetPlayers);
viewLoadingScreen(2518, "Network Game", NULL, buffer);
videoNextPage();
}
break;
}
default:
break;
}
}
enet_host_service(gNetENetServer, NULL, 0);
}
initprintf("All clients connected\n");
dassert(numplayers >= 1);
gInitialNetPlayers = gNetPlayers = numplayers;
connecthead = 0;
for (int i = 0; i < numplayers-1; i++)
connectpoint2[i] = i+1;
connectpoint2[numplayers-1] = -1;
enet_host_service(gNetENetServer, NULL, 0);
// Send connect info
char *pPacket = packet;
PutPacketByte(pPacket, BLOOD_SERVICE_CONNECTINFO);
PKT_CONNECTINFO connectinfo;
connectinfo.numplayers = numplayers;
connectinfo.connecthead = connecthead;
for (int i = 0; i < numplayers; i++)
connectinfo.connectpoint2[i] = connectpoint2[i];
PutPacketBuffer(pPacket, &connectinfo, sizeof(connectinfo));
ENetPacket *pEPacket = enet_packet_create(packet, pPacket-packet, ENET_PACKET_FLAG_RELIABLE);
enet_host_broadcast(gNetENetServer, BLOOD_ENET_SERVICE, pEPacket);
//enet_packet_destroy(pEPacket);
enet_host_service(gNetENetServer, NULL, 0);
// Send id
myconnectindex = 0;
for (int i = 1; i < numplayers; i++)
{
if (!gNetPlayerPeer[i])
continue;
char *pPacket = packet;
PutPacketByte(pPacket, BLOOD_SERVICE_CONNECTID);
PutPacketByte(pPacket, i);
ENetPacket *pEPacket = enet_packet_create(packet, pPacket-packet, ENET_PACKET_FLAG_RELIABLE);
enet_peer_send(gNetPlayerPeer[i], BLOOD_ENET_SERVICE, pEPacket);
enet_host_service(gNetENetServer, NULL, 0);
}
enet_host_service(gNetENetServer, NULL, 0);
}
else if (gNetMode == NETWORK_CLIENT)
{
ENetEvent event;
sprintf(buffer, "Connecting to %s:%u", gNetAddress, gNetPort);
initprintf("%s\n", buffer);
if (!bConsole)
{
viewLoadingScreen(2518, "Network Game", NULL, buffer);
videoNextPage();
}
gNetENetClient = enet_host_create(NULL, 1, BLOOD_ENET_CHANNEL_MAX, 0, 0);
enet_address_set_host(&gNetENetAddress, gNetAddress);
gNetENetAddress.port = gNetPort;
gNetENetPeer = enet_host_connect(gNetENetClient, &gNetENetAddress, BLOOD_ENET_CHANNEL_MAX, 0);
if (!gNetENetPeer)
{
initprintf("No available peers for initiating an ENet connection.\n");
netDeinitialize();
netResetToSinglePlayer();
return;
}
if (enet_host_service(gNetENetClient, &event, 5000) > 0 && event.type == ENET_EVENT_TYPE_CONNECT)
initprintf("Connected to %s:%i\n", gNetAddress, gNetPort);
else
{
initprintf("Could not connect to %s:%i\n", gNetAddress, gNetPort);
netDeinitialize();
return;
}
bool bWaitServer = true;
if (!bConsole)
{
viewLoadingScreen(2518, "Network Game", NULL, "Waiting for server response");
videoNextPage();
}
while (bWaitServer)
{
handleevents();
if (quitevent)
{
netClientDisconnect();
QuitGame();
}
if (!bConsole && KB_KeyPressed(sc_Escape))
{
netClientDisconnect();
netDeinitialize();
netResetToSinglePlayer();
return;
}
enet_host_service(gNetENetClient, NULL, 0);
if (enet_host_check_events(gNetENetClient, &event) > 0)
{
switch (event.type)
{
case ENET_EVENT_TYPE_DISCONNECT:
initprintf("Lost connection to server\n");
netDeinitialize();
netResetToSinglePlayer();
return;
case ENET_EVENT_TYPE_RECEIVE:
//initprintf("NETEVENT\n");
if (event.channelID == BLOOD_ENET_SERVICE)
{
char *pPacket = (char*)event.packet->data;
switch (GetPacketByte(pPacket))
{
case BLOOD_SERVICE_CONNECTINFO:
{
PKT_CONNECTINFO *connectinfo = (PKT_CONNECTINFO*)pPacket;
gInitialNetPlayers = gNetPlayers = numplayers = connectinfo->numplayers;
connecthead = connectinfo->connecthead;
for (int i = 0; i < numplayers; i++)
connectpoint2[i] = connectinfo->connectpoint2[i];
//initprintf("BLOOD_SERVICE_CONNECTINFO\n");
break;
}
case BLOOD_SERVICE_CONNECTID:
dassert(numplayers > 1);
myconnectindex = GetPacketByte(pPacket);
bWaitServer = false;
//initprintf("BLOOD_SERVICE_CONNECTID\n");
break;
}
}
enet_packet_destroy(event.packet);
break;
default:
break;
}
}
}
enet_host_service(gNetENetClient, NULL, 0);
initprintf("Successfully connected to server\n");
}
gNetENetInit = true;
gGameOptions.nGameType = 2;
#endif
}
void netDeinitialize(void)
{
#ifndef NETCODE_DISABLE
if (!gNetENetInit)
return;
gNetENetInit = false;
if (gNetMode != NETWORK_NONE)
{
if (gNetENetServer)
{
netServerDisconnect();
enet_host_destroy(gNetENetServer);
}
else if (gNetENetClient)
{
netClientDisconnect();
enet_host_destroy(gNetENetClient);
}
enet_deinitialize();
}
gNetENetServer = gNetENetClient = NULL;
#endif
}
#ifndef NETCODE_DISABLE
void netPostEPacket(ENetPacket *pEPacket)
{
//static int number;
//initprintf("netPostEPacket %i\n", number++);
gNetPacketFifo[gNetPacketHead] = pEPacket;
gNetPacketHead = (gNetPacketHead+1)%kENetFifoSize;
}
#endif
void netUpdate(void)
{
#ifndef NETCODE_DISABLE
ENetEvent event;
if (gNetMode == NETWORK_NONE)
return;
if (gNetENetServer)
{
enet_host_service(gNetENetServer, NULL, 0);
while (enet_host_check_events(gNetENetServer, &event) > 0)
{
switch (event.type)
{
case ENET_EVENT_TYPE_DISCONNECT:
{
int nPlayer;
for (nPlayer = connectpoint2[connecthead]; nPlayer >= 0; nPlayer = connectpoint2[nPlayer])
if (gNetPlayerPeer[nPlayer] == event.peer)
break;
for (int p = 0; p < kMaxPlayers; p++)
if (gNetPlayerPeer[p] == event.peer)
gNetPlayerPeer[p] = NULL;
if (nPlayer >= 0)
{
// TODO: Game most likely will go out of sync here...
char *pPacket = packet;
PutPacketByte(pPacket, 249);
PutPacketDWord(pPacket, nPlayer);
netSendPacketAll(packet, pPacket - packet);
netPlayerQuit(nPlayer);
netWaitForEveryone(0);
}
if (gNetPlayers <= 1)
{
netDeinitialize();
netResetToSinglePlayer();
return;
}
break;
}
case ENET_EVENT_TYPE_RECEIVE:
switch (event.channelID)
{
case BLOOD_ENET_SERVICE:
{
char *pPacket = (char*)event.packet->data;
if (GetPacketByte(pPacket) != BLOOD_SERVICE_SENDTOID)
ThrowError("Packet error\n");
int nDest = GetPacketByte(pPacket);
int nSource = GetPacketByte(pPacket);
int nSize = event.packet->dataLength-3;
if (gNetPlayerPeer[nDest] != NULL)
{
ENetPacket *pNetPacket = enet_packet_create(NULL, nSize + 1, ENET_PACKET_FLAG_RELIABLE);
char *pPBuffer = (char*)pNetPacket->data;
PutPacketByte(pPBuffer, nSource);
PutPacketBuffer(pPBuffer, pPacket, nSize);
enet_peer_send(gNetPlayerPeer[nDest], BLOOD_ENET_GAME, pNetPacket);
enet_host_service(gNetENetServer, NULL, 0);
}
enet_packet_destroy(event.packet);
break;
}
case BLOOD_ENET_GAME:
netPostEPacket(event.packet);
break;
}
default:
break;
}
enet_host_service(gNetENetServer, NULL, 0);
}
enet_host_service(gNetENetServer, NULL, 0);
}
else if (gNetENetClient)
{
enet_host_service(gNetENetClient, NULL, 0);
while (enet_host_check_events(gNetENetClient, &event) > 0)
{
switch (event.type)
{
case ENET_EVENT_TYPE_DISCONNECT:
initprintf("Lost connection to server\n");
netDeinitialize();
netResetToSinglePlayer();
gQuitGame = gRestartGame = true;
return;
case ENET_EVENT_TYPE_RECEIVE:
switch (event.channelID)
{
case BLOOD_ENET_SERVICE:
{
ThrowError("Packet error\n");
break;
}
case BLOOD_ENET_GAME:
netPostEPacket(event.packet);
break;
}
default:
break;
}
enet_host_service(gNetENetClient, NULL, 0);
}
enet_host_service(gNetENetClient, NULL, 0);
}
#endif
}
void faketimerhandler(void)
{
#ifndef NETCODE_DISABLE
if (gNetMode != NETWORK_NONE && gNetENetInit)
netUpdate();
#if 0
if (gGameClock >= gNetFifoClock && ready2send)
{
gNetFifoClock += 4;
netGetInput();
}
#endif
#endif
//if (gNetMode != NETWORK_NONE && gNetENetInit)
// enet_host_service(gNetMode == NETWORK_SERVER ? gNetENetServer : gNetENetClient, NULL, 0);
}
void netPlayerQuit(int nPlayer)
{
char buffer[128];
sprintf(buffer, "%s left the game with %d frags.", gProfile[nPlayer].name, gPlayer[nPlayer].fragCount);
viewSetMessage(buffer);
if (gGameStarted)
{
seqKill(3, gPlayer[nPlayer].pSprite->extra);
actPostSprite(gPlayer[nPlayer].nSprite, kStatFree);
if (nPlayer == gViewIndex)
gViewIndex = myconnectindex;
gView = &gPlayer[gViewIndex];
}
if (nPlayer == connecthead)
{
connecthead = connectpoint2[connecthead];
//if (gPacketMode == PACKETMODE_1)
{
//byte_27B2CC = 1;
gQuitGame = true;
gRestartGame = true;
gNetPlayers = 1;
//gQuitRequest = 1;
}
}
else
{
for (int p = connecthead; p >= 0; p = connectpoint2[p])
{
if (connectpoint2[p] == nPlayer)
connectpoint2[p] = connectpoint2[nPlayer];
}
#ifndef NETCODE_DISABLE
gNetPlayerPeer[nPlayer] = NULL;
#endif
}
gNetPlayers--;
numplayers = ClipLow(numplayers-1, 1);
if (gNetPlayers <= 1)
{
netDeinitialize();
netResetToSinglePlayer();
}
}
END_BLD_NS