//------------------------------------------------------------------------- /* 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 "build.h" #include "mmulti.h" #include "pragmas.h" #ifndef NETCODE_DISABLE #include "enet/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" char packet[576]; bool gStartNewGame = 0; PACKETMODE gPacketMode = PACKETMODE_1; int 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 = gGameClock = 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].at22; 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, "%s : %s", gProfile[nPlayer].name, pPacket); viewSetMessage(buffer); 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: keystatus[1] = 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, szPlayerName); pProfile->skill = gSkill; pProfile->nAutoAim = gAutoAim; pProfile->nWeaponSwitch = gWeaponSwitch; 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 (keystatus[sc_Escape] && a1) exit(0); G_HandleAsync(); 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) { int nClock = gGameClock; char *pPacket = packet; PutPacketByte(pPacket, 254); for (int i = 0; i < 8; i++) { if (nClock <= gGameClock) { nClock = gGameClock+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); gGameClock -= 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); gGameClock -= 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 (enet_initialize() != 0) { 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) { timerUpdate(); #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].at2c6); viewSetMessage(buffer); if (gGameStarted) { seqKill(3, gPlayer[nPlayer].pSprite->extra); actPostSprite(gPlayer[nPlayer].at5b, 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(); } }