Descent3/netcon/mtclient/mtgametrack.cpp
2024-09-06 13:28:50 -04:00

432 lines
13 KiB
C++

/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* 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 3 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
--- HISTORICAL COMMENTS FOLLOW ---
* $Logfile: /DescentIII/Main/mtclient/mtgametrack.cpp $
* $Revision: 1.1.1.1 $
* $Date: 2003-08-26 03:58:40 $
* $Author: kevinb $
*
* Game Tracker client code
*
* $Log: not supported by cvs2svn $
*
* 17 10/03/01 12:44a Kevin
* Smaller version of pxo packets
*
* 16 9/02/99 3:16p Kevin
* Macintosh byte ordering fixes
*
* 15 8/25/99 2:38p Samir
* added byteswap.h for INTEL_INT.
*
* 14 8/24/99 5:47p Kevin
* Macintosh crap
*
* 13 7/06/99 5:52p Kevin
* PXO & multiplayer fixes for the patch
*
* 12 5/05/99 5:23p Kevin
* fixed some bugs on the gamelist
*
* 11 5/04/99 7:29p Kevin
* Fixed bug that caused the server to send his game info twice whenever
* the level starts.
*
* 10 5/04/99 5:39p Kevin
* connection dll & pxo game tracking improvements (added server type to
* list)
*
* 9 4/19/99 8:35p Jeff
* Compile under Linux
*
* 8 3/25/99 3:26p Kevin
* Made PXO games be based on your chat channel
*
* 7 1/07/99 11:51a Kevin
* Added support for joining servers on alternate ports and hosting behind
* a proxy/firewall
*
* 6 12/30/98 12:16p Kevin
* Auto Mission Download system
*
* 5 12/23/98 6:38p Kevin
* All UDP data (except gamespy) now uses one (registered) port number
*
* 4 10/19/98 9:50a Kevin
* Added nettest application
*
* 3 7/27/98 5:31p Kevin
* Sound/Bitmap exchange system
*
* 2 6/01/98 10:10a Kevin
* Added DLL connection interface and auto update DLL
*
* 1 5/21/98 11:08a Kevin
*
* 6 5/14/98 11:07a Kevin
* Made gameover packet to the tracker reliable
*
* 5 5/04/98 12:53p Kevin
* Fixed mastertracker resend stuff
*
* 4 4/30/98 3:49p Kevin
* Mastertracker pilot stats
*
* 3 4/24/98 3:50p Kevin
* Added mastertracker game tracking support
*
* 2 4/23/98 4:49p Kevin
* Added Master tracker Game tracker support
*
* 1 4/23/98 3:26p Kevin
* Initial version
*
* $NoKeywords: $
*/
/*
InitGameTracker(int game_type); //D3 or Freespace
//Call periodically so we can send our update, and make sure it is received
IdleGameTracker();
StartTrackerGame(void *buffer) //Call with a freespace_net_game_data or d3_net_game_data structure
//Call with a freespace_net_game_data or d3_net_game_data structure
//Updates our memory so when it is time, we send the latest game data
UpdateGameData(gamedata);
//Call to signify end of game
RequestGameList();//Sends a GNT_GAMELIST_REQ packet to the server.
game_list * GetGameList();//returns a pointer to a game_list struct
*/
#ifdef WIN32
#include <windows.h>
#endif
#include <cstring>
#include <ctime>
#include "pstypes.h"
#include "networking.h"
#include "mtgametrack.h"
#include "byteswap.h"
#include "mt_net.h"
// extern float DLLtimer_GetTime(void);
typedef float (*timer_GetTime_fp)();
extern timer_GetTime_fp DLLtimer_GetTime;
typedef void (*HandleGamePacket_fp)(uint8_t *data, int len, network_address *from);
typedef int (*nw_RegisterCallback_fp)(HandleGamePacket_fp, uint8_t id);
extern nw_RegisterCallback_fp DLLnw_RegisterCallback;
typedef int (*nw_DoReceiveCallbacks_fp)();
extern nw_DoReceiveCallbacks_fp DLLnw_DoReceiveCallbacks;
typedef int (*nw_SendWithID_fp)(uint8_t id, uint8_t *data, int len, network_address *who_to);
extern nw_SendWithID_fp DLLnw_SendWithID;
#if (defined(LOGGER) && (!defined(RELEASE)))
#define DLLmprintf(...) DLLDebug_ConsolePrintf(__VA_ARGS__)
#else
#define DLLmprintf(...)
#endif
typedef void (*Debug_ConsolePrintf_fp)(int n, const char *format, ...);
extern Debug_ConsolePrintf_fp DLLDebug_ConsolePrintf;
// Variables
game_list GameBuffer[MAX_GAME_BUFFERS];
int GameType; // d3 or fs
float LastTrackerUpdate;
float LastSentToTracker;
float LastGameOverPacket;
float FirstGameOverPacket;
int SendingGameOver;
uint32_t TrackerAckdUs;
uint32_t TrackerGameIsRunning;
game_packet_header TrackerGameData;
game_packet_header GameListReq;
game_packet_header TrackAckPacket;
game_packet_header GameOverPacket;
game_packet_header inpacket;
d3_net_game_data_tiny *D3TrackerGameData;
extern int DLLnw_ListenPort;
extern uint16_t DLLPXOPort;
extern char *DLLPXO_hosted_lobby_name;
int InitGameTrackerClient(int gametype) {
GameType = gametype;
LastTrackerUpdate = 0;
switch (gametype) {
case GT_FREESPACE:
// TrackerGameData.len = GAME_HEADER_ONLY_SIZE+sizeof(freespace_net_game_data);
break;
case GT_DESCENT3:
TrackerGameData.len = INTEL_INT(GAME_HEADER_ONLY_SIZE + sizeof(d3_net_game_data_short));
break;
case GT_D3TNG:
TrackerGameData.len = INTEL_INT(GAME_HEADER_ONLY_SIZE + sizeof(d3_net_game_data_tiny));
break;
}
TrackerGameData.game_type = gametype; // 1==freespace (GT_FREESPACE), 2==D3, 3==tuberacer, etc.
TrackerGameData.type = INTEL_INT(GNT_GAMEUPDATE_TINY); // Used to specify what to do ie. Add a new net game
// (GNT_GAMESTARTED), remove a net game (game over), etc.
D3TrackerGameData = (d3_net_game_data_tiny *)&TrackerGameData.data;
if (DLLPXOPort) {
D3TrackerGameData->options = 1;
D3TrackerGameData->arg1 = DLLPXOPort;
}
GameListReq.game_type = gametype;
GameListReq.type = INTEL_INT(GNT_GAMELIST_LITE_FILTER_REQ);
GameListReq.len = INTEL_INT(GAME_HEADER_ONLY_SIZE);
TrackAckPacket.game_type = gametype;
TrackAckPacket.len = INTEL_INT(GAME_HEADER_ONLY_SIZE);
TrackAckPacket.type = INTEL_INT(GNT_CLIENT_ACK);
GameOverPacket.game_type = gametype;
GameOverPacket.len = INTEL_INT(GAME_HEADER_ONLY_SIZE);
GameOverPacket.type = INTEL_INT(GNT_GAMEOVER);
SendingGameOver = 0;
DLLnw_RegisterCallback(HandleGamePacket, PXO_NETID_GAME_TRACKER);
return 1;
}
void IdleGameTracker() {
DLLnw_DoReceiveCallbacks();
// uint32_t bytesin;
// mprintf(0,"IdleGameTracker() entered.\n");
if ((TrackerGameIsRunning) && ((DLLtimer_GetTime() - LastTrackerUpdate) > TRACKER_UPDATE_INTERVAL)) {
struct tm *newtime;
time_t aclock;
time(&aclock); /* Get time in seconds */
newtime = localtime(&aclock);
// Time to update the tracker again
DLLmprintf(0, "[%.24s] Updating the PXO game tracker!\n", asctime(newtime));
SendGameTrackerPacker(&TrackerGameData);
// sendto(gamesock,(const char *)&TrackerGameData,TrackerGameData.len,0,(SOCKADDR
//*)&gtrackaddr,sizeof(SOCKADDR_IN));
TrackerAckdUs = 0;
LastTrackerUpdate = DLLtimer_GetTime();
LastSentToTracker = DLLtimer_GetTime();
} else if ((TrackerGameIsRunning) && (!TrackerAckdUs) &&
((DLLtimer_GetTime() - LastSentToTracker) > TRACKER_RESEND_TIME)) {
// We still haven't been acked by the last packet and it's time to resend.
struct tm *newtime;
time_t aclock;
time(&aclock); /* Get time in seconds */
newtime = localtime(&aclock);
// Time to update the tracker again
DLLmprintf(0, "[%.24s] (Resending) Updating the PXO game tracker!\n", asctime(newtime));
SendGameTrackerPacker(&TrackerGameData);
TrackerAckdUs = 0;
LastTrackerUpdate = DLLtimer_GetTime();
LastSentToTracker = DLLtimer_GetTime();
}
if (SendingGameOver) {
if ((DLLtimer_GetTime() - LastGameOverPacket) > TRACKER_RESEND_TIME) {
// resend
LastGameOverPacket = DLLtimer_GetTime();
SendGameTrackerPacker(&GameOverPacket);
} else if ((DLLtimer_GetTime() - FirstGameOverPacket) > TRACKER_TIMEOUT) {
// Giving up, it timed out.
SendingGameOver = 3;
}
}
}
static void SendClientHolePunch(SOCKADDR_IN *addr) {
game_packet_header HolePunchAck;
network_address send_address;
memset(&send_address, 0, sizeof(network_address));
memcpy(send_address.address, &addr->sin_addr, 4);
send_address.port = htons(addr->sin_port);
send_address.connection_type = NP_TCP;
HolePunchAck.game_type = GameType;
HolePunchAck.type = INTEL_INT(GNT_NAT_HOLE_PUNCH_ACK);
HolePunchAck.len = INTEL_INT(GAME_HEADER_ONLY_SIZE);
HolePunchAck.sig = 0; // to make sure tracker ignores this packet when it's ACK'd there
DLLnw_SendWithID(PXO_NETID_GAME_TRACKER, (uint8_t *)&HolePunchAck, INTEL_INT(HolePunchAck.len), &send_address);
}
void HandleGamePacket(uint8_t *data, int len, network_address *from) {
memcpy(&inpacket, data, sizeof(inpacket));
// Check to make sure the packets ok
if (len) {
switch (INTEL_INT(inpacket.type)) {
case GNT_SERVER_ACK:
// The server got our packet so we can stop sending now
TrackerAckdUs = 1;
if (SendingGameOver) {
SendingGameOver = 2;
}
break;
case GNT_GAMELIST_LITE_DATA:
if (len < (GAME_HEADER_ONLY_SIZE + sizeof(game_list))) {
DLLmprintf(0, "(%d) packet is too small!\n", len);
}
// Woohoo! Game data! put it in the buffer (if one's free)
// DLLmprintf(0,"GNT_GAMELIST_LITE_DATA received.\n");
for (int i = 0; i < MAX_GAME_BUFFERS; i++) {
if (GameBuffer[i].game_type == GT_UNUSED) {
memset(&GameBuffer[i], 0, sizeof(game_list));
memcpy(&GameBuffer[i], inpacket.data, len - GAME_HEADER_ONLY_SIZE);
i = MAX_GAME_BUFFERS + 1;
}
}
break;
case GNT_NAT_HOLE_PUNCH_REQ:
if (len == (GAME_HEADER_ONLY_SIZE+sizeof(hole_punch_addr_ip6))) {
// we don't support IPv6 yet so skip this one
} else {
SOCKADDR_IN nataddr;
auto ipv4 = reinterpret_cast<hole_punch_addr *>(&inpacket.data);
nataddr.sin_addr.s_addr = ipv4->addr;
nataddr.sin_port = ipv4->port;
nataddr.sin_family = AF_INET;
SendClientHolePunch(&nataddr);
}
break;
}
// mprintf(0,"Sending ACK.\n");
AckPacket(inpacket.sig);
}
}
void UpdateGameData(void *buffer) {
if (GameType == GT_FREESPACE) {
// memcpy(FreeSpaceTrackerGameData,buffer,sizeof(freespace_net_game_data));
} else if (GameType == GT_DESCENT3) {
memcpy(D3TrackerGameData, buffer, sizeof(d3_net_game_data_short));
} else if (GameType == GT_D3TNG) {
memcpy(D3TrackerGameData, buffer, sizeof(d3_net_game_data_tiny));
}
}
game_list gl;
game_list *GetGameList() {
memset(&gl, 0, sizeof(gl));
for (auto & i : GameBuffer) {
if (i.game_type != GT_UNUSED) {
memcpy(&gl, &i, sizeof(gl));
i.game_type = GT_UNUSED;
return &gl;
}
}
return nullptr;
}
void RequestGameList() {
// Zero out all games
for (auto & i : GameBuffer) {
memset(&i, 0, sizeof(game_list));
}
int len = strlen(DLLPXO_hosted_lobby_name) + 1;
memcpy(GameListReq.data, DLLPXO_hosted_lobby_name, len);
GameListReq.len = INTEL_INT((uint32_t)(GAME_HEADER_ONLY_SIZE + len));
SendGameTrackerPacker(&GameListReq);
}
int SendGameOver() {
IdleGameTracker();
if (SendingGameOver == 3) {
// Request timed out.
return 2;
}
if (SendingGameOver == 2) {
SendingGameOver = 0;
return 1;
}
if (SendingGameOver == 1) {
// Wait until it's sent.
return 0;
}
if (SendingGameOver == 0) {
LastGameOverPacket = DLLtimer_GetTime();
FirstGameOverPacket = DLLtimer_GetTime();
SendingGameOver = 1;
TrackerGameIsRunning = 0;
SendGameTrackerPacker(&GameOverPacket);
return 0;
}
return 0;
}
void AckPacket(int sig) {
TrackAckPacket.sig = sig;
SendGameTrackerPacker(&TrackAckPacket);
}
void StartTrackerGame(void *buffer) {
struct tm *newtime;
time_t aclock;
time(&aclock); /* Get time in seconds */
newtime = localtime(&aclock);
// Time to update the tracker again
DLLmprintf(0, "[%.24s] Updating the PXO game tracker with a new game!\n", asctime(newtime));
if (GameType == GT_FREESPACE) {
// memcpy(FreeSpaceTrackerGameData,buffer,sizeof(freespace_net_game_data));
} else if (GameType == GT_DESCENT3) {
memcpy(D3TrackerGameData, buffer, sizeof(d3_net_game_data_short));
SendGameTrackerPacker(&TrackerGameData);
TrackerAckdUs = 0;
LastTrackerUpdate = DLLtimer_GetTime();
LastSentToTracker = DLLtimer_GetTime();
} else if (GameType == GT_D3TNG) {
memcpy(D3TrackerGameData, buffer, sizeof(d3_net_game_data_tiny));
SendGameTrackerPacker(&TrackerGameData);
TrackerAckdUs = 0;
LastTrackerUpdate = DLLtimer_GetTime();
LastSentToTracker = DLLtimer_GetTime();
}
TrackerGameIsRunning = 1;
// LastTrackerUpdate = 0;
}