int gamemode;
char *gamename;
char *gamedirname;
+char com_modname[MAX_OSPATH];
/*
com_basedir[i-1] = 0;
// start up with GAMENAME by default (id1)
+ strcpy(com_modname, GAMENAME);
COM_AddGameDirectory (va("%s/"GAMENAME, com_basedir));
if (gamedirname[0])
{
com_modified = true;
+ strcpy(com_modname, gamedirname);
COM_AddGameDirectory (va("%s/%s", com_basedir, gamedirname));
}
if (i && i < com_argc-1)
{
com_modified = true;
+ strcpy(com_modname, com_argv[i+1]);
COM_AddGameDirectory (va("%s/%s", com_basedir, com_argv[i+1]));
}
extern int gamemode;
extern char *gamename;
+extern char com_modname[MAX_OSPATH];
// LordHavoc: useful...
void COM_ToLowerString(const char *in, char *out);
# End Source File\r
# Begin Source File\r
\r
+SOURCE=.\net_master.c\r
+# End Source File\r
+# Begin Source File\r
+\r
SOURCE=.\net_win.c\r
# End Source File\r
# Begin Source File\r
# End Source File\r
# Begin Source File\r
\r
+SOURCE=.\net_master.h\r
+# End Source File\r
+# Begin Source File\r
+\r
SOURCE=.\net_wins.h\r
# End Source File\r
# Begin Source File\r
MSG_WriteByte (&client->message, host_client - svs.clients);
MSG_WriteByte (&client->message, 0);
}
+
+ NET_Heartbeat ();
}
/*
// stop all client sounds immediately
CL_Disconnect ();
+ NET_Heartbeat ();
+ NET_Heartbeat ();
+
// flush any pending messages - like the score!!!
start = Sys_DoubleTime();
do
CLIENTOBJECTS= cgame.o cgamevm.o chase.o cl_collision.o cl_demo.o cl_input.o cl_main.o cl_parse.o cl_particles.o cl_screen.o cl_video.o console.o dpvsimpledecode.o fractalnoise.o gl_backend.o gl_draw.o gl_models.o gl_rmain.o gl_rsurf.o gl_textures.o keys.o menu.o meshqueue.o r_crosshairs.o r_explosion.o r_explosion.o r_lerpanim.o r_light.o r_modules.o r_sky.o r_sprites.o sbar.o ui.o vid_shared.o view.o wavefile.o
SERVEROBJECTS= pr_cmds.o pr_edict.o pr_exec.o sv_light.o sv_main.o sv_move.o sv_phys.o sv_user.o
-SHAREDOBJECTS= builddate.o cmd.o collision.o common.o crc.o cvar.o filematch.o host.o host_cmd.o image.o mathlib.o matrixlib.o model_alias.o model_brush.o model_shared.o model_sprite.o net_bsd.o net_dgrm.o net_loop.o net_main.o net_udp.o palette.o portals.o protocol.o quakeio.o sys_linux.o sys_shared.o transform.o world.o wad.o zone.o $(NETOBJECTS) $(SERVEROBJECTS)
+SHAREDOBJECTS= builddate.o cmd.o collision.o common.o crc.o cvar.o filematch.o host.o host_cmd.o image.o mathlib.o matrixlib.o model_alias.o model_brush.o model_shared.o model_sprite.o net_bsd.o net_dgrm.o net_loop.o net_main.o net_master.o net_udp.o palette.o portals.o protocol.o quakeio.o sys_linux.o sys_shared.o transform.o world.o wad.o zone.o $(NETOBJECTS) $(SERVEROBJECTS)
#K6/athlon optimizations
void M_Menu_LanConfig_f (void);
void M_Menu_GameOptions_f (void);
void M_Menu_Search_f (void);
+void M_Menu_InetSearch_f (void);
void M_Menu_ServerList_f (void);
void M_Main_Draw (void);
void M_LanConfig_Draw (void);
void M_GameOptions_Draw (void);
void M_Search_Draw (void);
+void M_InetSearch_Draw (void);
void M_ServerList_Draw (void);
void M_Main_Key (int key);
void M_LanConfig_Key (int key);
void M_GameOptions_Key (int key);
void M_Search_Key (int key);
+void M_InetSearch_Key (int key);
void M_ServerList_Key (int key);
qboolean m_entersound; // play after drawing a frame, so caching
if (ipxAvailable || tcpipAvailable)
return;
- M_PrintWhite ((320/2) - ((27*8)/2), 148, "No Communications Available");
+ M_PrintWhite ((320/2) - ((27*8)/2), 168, "No Communications Available");
}
/* LAN CONFIG MENU */
int lanConfig_cursor = -1;
-int lanConfig_cursor_table [] = {72, 92, 124};
-#define NUM_LANCONFIG_CMDS 3
+int lanConfig_cursor_table [] = {72, 92, 112, 144};
+#define NUM_LANCONFIG_CMDS 4
int lanConfig_port;
char lanConfig_portname[6];
if (JoiningGame)
{
M_Print (basex, lanConfig_cursor_table[1], "Search for local games...");
- M_Print (basex, 108, "Join game at:");
- M_DrawTextBox (basex+8, lanConfig_cursor_table[2]-8, 22, 1);
- M_Print (basex+16, lanConfig_cursor_table[2], lanConfig_joinname);
+ M_Print (basex, lanConfig_cursor_table[2], "Search for internet games...");
+ M_Print (basex, 128, "Join game at:");
+ M_DrawTextBox (basex+8, lanConfig_cursor_table[3]-8, 22, 1);
+ M_Print (basex+16, lanConfig_cursor_table[3], lanConfig_joinname);
}
else
{
if (lanConfig_cursor == 0)
M_DrawCharacter (basex+9*8 + 8*strlen(lanConfig_portname), lanConfig_cursor_table [0], 10+((int)(realtime*4)&1));
- if (lanConfig_cursor == 2)
- M_DrawCharacter (basex+16 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [2], 10+((int)(realtime*4)&1));
+ if (lanConfig_cursor == 3)
+ M_DrawCharacter (basex+16 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [3], 10+((int)(realtime*4)&1));
if (*m_return_reason)
- M_PrintWhite (basex, 148, m_return_reason);
+ M_PrintWhite (basex, 168, m_return_reason);
}
M_ConfigureNetSubsystem ();
- if (lanConfig_cursor == 1)
+ if (lanConfig_cursor == 1 || lanConfig_cursor == 2)
{
if (StartingGame)
{
M_Menu_GameOptions_f ();
break;
}
- M_Menu_Search_f();
+ if (lanConfig_cursor == 1)
+ M_Menu_Search_f();
+ else
+ M_Menu_InetSearch_f();
break;
}
- if (lanConfig_cursor == 2)
+ if (lanConfig_cursor == 3)
{
m_return_state = m_state;
m_return_onerror = true;
lanConfig_portname[strlen(lanConfig_portname)-1] = 0;
}
- if (lanConfig_cursor == 2)
+ if (lanConfig_cursor == 3)
{
if (strlen(lanConfig_joinname))
lanConfig_joinname[strlen(lanConfig_joinname)-1] = 0;
if (key < 32 || key > 127)
break;
- if (lanConfig_cursor == 2)
+ if (lanConfig_cursor == 3)
{
l = strlen(lanConfig_joinname);
if (l < 21)
}
}
- if (StartingGame && lanConfig_cursor == 2)
+ if (StartingGame && lanConfig_cursor == 3)
{
if (key == K_UPARROW)
lanConfig_cursor = 1;
{
}
+//=============================================================================
+/* INTERNET SEARCH MENU */
+
+void M_Menu_InetSearch_f (void)
+{
+ key_dest = key_menu;
+ m_state = m_search;
+ m_entersound = false;
+ slistSilent = true;
+ slistLocal = false;
+ searchComplete = false;
+ NET_InetSlist_f();
+
+}
+
+
+void M_InetSearch_Draw (void)
+{
+ cachepic_t *p;
+ int x;
+
+ p = Draw_CachePic ("gfx/p_multi.lmp");
+ M_DrawPic ( (320-p->width)/2, 4, "gfx/p_multi.lmp");
+ x = (320/2) - ((12*8)/2) + 4;
+ M_DrawTextBox (x-8, 32, 12, 1);
+ M_Print (x, 40, "Searching...");
+
+ if(slistInProgress)
+ {
+ NET_Poll();
+ return;
+ }
+
+ if (! searchComplete)
+ {
+ searchComplete = true;
+ searchCompleteTime = realtime;
+ }
+
+ if (hostCacheCount)
+ {
+ M_Menu_ServerList_f ();
+ return;
+ }
+
+ M_PrintWhite ((320/2) - ((22*8)/2), 64, "No Quake servers found");
+ if ((realtime - searchCompleteTime) < 3.0)
+ return;
+
+ M_Menu_LanConfig_f ();
+}
+
+
+void M_InetSearch_Key (int key)
+{
+}
+
//=============================================================================
/* SLIST MENU */
M_DrawCharacter (0, 32 + slist_cursor*8, 12+((int)(realtime*4)&1));
if (*m_return_reason)
- M_PrintWhite (16, 148, m_return_reason);
+ M_PrintWhite (16, 168, m_return_reason);
}
int (*CloseSocket) (int socket);
int (*Connect) (int socket, struct qsockaddr *addr);
int (*CheckNewConnections) (void);
+ int (*Recv) (qbyte *buf, int len, struct qsockaddr *addr);
+ int (*Send) (qbyte *buf, int len, struct qsockaddr *addr);
int (*Read) (int socket, qbyte *buf, int len, struct qsockaddr *addr);
int (*Write) (int socket, qbyte *buf, int len, struct qsockaddr *addr);
int (*Broadcast) (int socket, qbyte *buf, int len);
int (*Init) (void);
void (*Listen) (qboolean state);
void (*SearchForHosts) (qboolean xmit);
+ qboolean (*SearchForInetHosts) (char *master);
qsocket_t *(*Connect) (char *host);
qsocket_t *(*CheckNewConnections) (void);
int (*QGetMessage) (qsocket_t *sock);
qboolean (*CanSendUnreliableMessage) (qsocket_t *sock);
void (*Close) (qsocket_t *sock);
void (*Shutdown) (void);
+ void (*Heartbeat) (char *host);
int controlSock;
} net_driver_t;
struct qsocket_s *NET_Connect (char *host);
// called by client to connect to a host. Returns -1 if not able to
+void NET_Heartbeat (void);
+// Send an heartbeat to the master server(s)
+
qboolean NET_CanSendMessage (qsocket_t *sock);
// Returns true or false if the given qsocket can currently accept a
// message to be transmitted.
extern cvar_t hostname;
void NET_Slist_f (void);
+void NET_InetSlist_f (void);
#endif
Loop_Init,
Loop_Listen,
Loop_SearchForHosts,
+ Loop_SearchForInetHosts,
Loop_Connect,
Loop_CheckNewConnections,
Loop_GetMessage,
Loop_CanSendMessage,
Loop_CanSendUnreliableMessage,
Loop_Close,
- Loop_Shutdown
+ Loop_Shutdown,
+ Loop_Heartbeat
}
,
{
Datagram_Init,
Datagram_Listen,
Datagram_SearchForHosts,
+ Datagram_SearchForInetHosts,
Datagram_Connect,
Datagram_CheckNewConnections,
Datagram_GetMessage,
Datagram_CanSendMessage,
Datagram_CanSendUnreliableMessage,
Datagram_Close,
- Datagram_Shutdown
+ Datagram_Shutdown,
+ Datagram_Heartbeat
}
};
UDP_CloseSocket,
UDP_Connect,
UDP_CheckNewConnections,
+ UDP_Recv,
+ UDP_Send,
UDP_Read,
UDP_Write,
UDP_Broadcast,
#include "quakedef.h"
#include "net_dgrm.h"
+#include "net_master.h"
cvar_t cl_port = {CVAR_SAVE, "cl_port", "0"};
MSG_BeginReading ();
control = BigLong(*((int *)net_message.data));
MSG_ReadLong();
- if (control == -1)
+
+ // Messages starting by 0xFFFFFFFF are master server messages
+ if (control == 0xFFFFFFFF)
+ {
+ int responsesize = Master_HandleMessage();
+ if (responsesize > 0)
+ {
+ dfunc.Write(acceptsock, net_message.data, responsesize, &clientaddr);
+ SZ_Clear(&net_message);
+ }
return NULL;
+ }
if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
return NULL;
if ((control & NETFLAG_LENGTH_MASK) != len)
if (strcmp(MSG_ReadString(), "QUAKE") != 0)
return NULL;
+ Con_DPrintf("Datagram_CheckNewConnections: received CCREQ_SERVERINFO, replying.\n");
+
SZ_Clear(&net_message);
// save space for the header, filled in later
MSG_WriteLong(&net_message, 0);
}
+static qboolean Datagram_HandleServerInfo (struct qsockaddr *readaddr)
+{
+ struct qsockaddr myaddr;
+ int control;
+ int c, n, i;
+
+ if (net_message.cursize < sizeof(int))
+ return false;
+
+ // don't answer our own query
+ dfunc.GetSocketAddr (dfunc.controlSock, &myaddr);
+ //if (dfunc.AddrCompare(readaddr, &myaddr) >= 0)
+ // return false;
+
+ // is the cache full?
+ if (hostCacheCount == HOSTCACHESIZE)
+ return false;
+
+ MSG_BeginReading ();
+ control = BigLong(*((int *)net_message.data));
+ MSG_ReadLong();
+ if (control == -1)
+ return false;
+ if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
+ return false;
+ if ((control & NETFLAG_LENGTH_MASK) != net_message.cursize)
+ return false;
+
+ c = MSG_ReadByte();
+ if (c != CCREP_SERVER_INFO)
+ return false;
+
+ dfunc.GetAddrFromName(MSG_ReadString(), readaddr);
+ // search the cache for this server
+ for (n = 0; n < hostCacheCount; n++)
+ if (dfunc.AddrCompare(readaddr, &hostcache[n].addr) == 0)
+ break;
+
+ // is it already there?
+ if (n < hostCacheCount)
+ return false;;
+
+ // add it
+ hostCacheCount++;
+ strcpy(hostcache[n].name, MSG_ReadString());
+ strcpy(hostcache[n].map, MSG_ReadString());
+ hostcache[n].users = MSG_ReadByte();
+ hostcache[n].maxusers = MSG_ReadByte();
+ c = MSG_ReadByte();
+ if (c != NET_PROTOCOL_VERSION)
+ {
+ strcpy(hostcache[n].cname, hostcache[n].name);
+ hostcache[n].cname[14] = 0;
+ strcpy(hostcache[n].name, "*");
+ strcat(hostcache[n].name, hostcache[n].cname);
+ }
+ memcpy(&hostcache[n].addr, readaddr, sizeof(struct qsockaddr));
+ hostcache[n].driver = net_driverlevel;
+ hostcache[n].ldriver = net_landriverlevel;
+ strcpy(hostcache[n].cname, dfunc.AddrToString(readaddr));
+
+ // check for a name conflict
+ for (i = 0; i < hostCacheCount; i++)
+ {
+ if (i == n)
+ continue;
+ if (Q_strcasecmp (hostcache[n].name, hostcache[i].name) == 0)
+ {
+ i = strlen(hostcache[n].name);
+ if (i < 15 && hostcache[n].name[i-1] > '8')
+ {
+ hostcache[n].name[i] = '0';
+ hostcache[n].name[i+1] = 0;
+ }
+ else
+ hostcache[n].name[i-1]++;
+ i = -1;
+ }
+ }
+
+ return true;
+}
+
+
static void _Datagram_SearchForHosts (qboolean xmit)
{
int ret;
- int n;
- int i;
struct qsockaddr readaddr;
- struct qsockaddr myaddr;
- int control;
- int c;
- dfunc.GetSocketAddr (dfunc.controlSock, &myaddr);
if (xmit)
{
SZ_Clear(&net_message);
while ((ret = dfunc.Read (dfunc.controlSock, net_message.data, net_message.maxsize, &readaddr)) > 0)
{
- if (ret < sizeof(int))
- continue;
net_message.cursize = ret;
+ Datagram_HandleServerInfo (&readaddr);
+ }
+}
- // don't answer our own query
- if (dfunc.AddrCompare(&readaddr, &myaddr) >= 0)
- continue;
-
- // is the cache full?
+void Datagram_SearchForHosts (qboolean xmit)
+{
+ for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+ {
if (hostCacheCount == HOSTCACHESIZE)
- continue;
-
- MSG_BeginReading ();
- control = BigLong(*((int *)net_message.data));
- MSG_ReadLong();
- if (control == -1)
- continue;
- if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
- continue;
- if ((control & NETFLAG_LENGTH_MASK) != ret)
- continue;
-
- c = MSG_ReadByte();
- if (c != CCREP_SERVER_INFO)
- continue;
+ break;
+ if (net_landrivers[net_landriverlevel].initialized)
+ _Datagram_SearchForHosts (xmit);
+ }
+}
- dfunc.GetAddrFromName(MSG_ReadString(), &readaddr);
- // search the cache for this server
- for (n = 0; n < hostCacheCount; n++)
- if (dfunc.AddrCompare(&readaddr, &hostcache[n].addr) == 0)
- break;
- // is it already there?
- if (n < hostCacheCount)
- continue;
+static qboolean _Datagram_SearchForInetHosts (char *master)
+{
+ qboolean result = false;
+ struct qsockaddr masteraddr;
+ struct qsockaddr readaddr;
+ int ret;
- // add it
- hostCacheCount++;
- strcpy(hostcache[n].name, MSG_ReadString());
- strcpy(hostcache[n].map, MSG_ReadString());
- hostcache[n].users = MSG_ReadByte();
- hostcache[n].maxusers = MSG_ReadByte();
- c = MSG_ReadByte();
- if (c != NET_PROTOCOL_VERSION)
+ if (master)
+ {
+ if (dfunc.GetAddrFromName(master, &masteraddr) != -1)
{
- strcpy(hostcache[n].cname, hostcache[n].name);
- hostcache[n].cname[14] = 0;
- strcpy(hostcache[n].name, "*");
- strcat(hostcache[n].name, hostcache[n].cname);
+ int portnum = 0;
+ const char* port = strrchr (master, ':');
+ if (port)
+ portnum = atoi (port + 1);
+ if (!portnum)
+ portnum = MASTER_PORT;
+ Con_DPrintf("Datagram_SearchForInetHosts: sending %d byte message to master %s\n", net_message.cursize, master);
+ dfunc.SetSocketPort (&masteraddr, portnum);
+ dfunc.Send (net_message.data, net_message.cursize, &masteraddr);
}
- memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr));
- hostcache[n].driver = net_driverlevel;
- hostcache[n].ldriver = net_landriverlevel;
- strcpy(hostcache[n].cname, dfunc.AddrToString(&readaddr));
+ }
- // check for a name conflict
- for (i = 0; i < hostCacheCount; i++)
- {
- if (i == n)
- continue;
- if (Q_strcasecmp (hostcache[n].name, hostcache[i].name) == 0)
- {
- i = strlen(hostcache[n].name);
- if (i < 15 && hostcache[n].name[i-1] > '8')
- {
- hostcache[n].name[i] = '0';
- hostcache[n].name[i+1] = 0;
- }
- else
- hostcache[n].name[i-1]++;
- i = -1;
- }
- }
+ while ((ret = dfunc.Recv (net_message.data, net_message.maxsize, &readaddr)) > 0)
+ {
+ net_message.cursize = ret;
+ Con_DPrintf("Datagram_SearchForInetHosts: Recv received %d byte message\n", net_message.cursize);
+ Master_ParseServerList (&dfunc);
}
+
+ while ((ret = dfunc.Read (dfunc.controlSock, net_message.data, net_message.maxsize, &readaddr)) > 0)
+ {
+ net_message.cursize = ret;
+ Con_DPrintf("Datagram_SearchForInetHosts: Read received %d byte message\n", net_message.cursize);
+ if (Datagram_HandleServerInfo (&readaddr))
+ result = true;
+ }
+
+ return result;
}
-void Datagram_SearchForHosts (qboolean xmit)
+
+qboolean Datagram_SearchForInetHosts (char *master)
{
+ qboolean result = false;
for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
{
if (hostCacheCount == HOSTCACHESIZE)
break;
if (net_landrivers[net_landriverlevel].initialized)
- _Datagram_SearchForHosts (xmit);
+ if (_Datagram_SearchForInetHosts (master))
+ result = true;
}
+
+ return result;
}
return ret;
}
+static void _Datagram_Heartbeat (char *master)
+{
+ struct qsockaddr masteraddr;
+ int portnum;
+ const char* port;
+
+ if (dfunc.GetAddrFromName(master, &masteraddr) == -1)
+ return;
+
+ portnum = 0;
+ port = strrchr (master, ':');
+ if (port)
+ portnum = atoi (port + 1);
+ if (!portnum)
+ portnum = MASTER_PORT;
+ dfunc.SetSocketPort (&masteraddr, portnum);
+
+ dfunc.Send (net_message.data, net_message.cursize, &masteraddr);
+}
+
+void Datagram_Heartbeat (char *master)
+{
+ for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+ if (net_landrivers[net_landriverlevel].initialized)
+ _Datagram_Heartbeat (master);
+}
int Datagram_Init (void);
void Datagram_Listen (qboolean state);
void Datagram_SearchForHosts (qboolean xmit);
+qboolean Datagram_SearchForInetHosts (char *master);
qsocket_t *Datagram_Connect (char *host);
qsocket_t *Datagram_CheckNewConnections (void);
int Datagram_GetMessage (qsocket_t *sock);
qboolean Datagram_CanSendUnreliableMessage (qsocket_t *sock);
void Datagram_Close (qsocket_t *sock);
void Datagram_Shutdown (void);
+void Datagram_Heartbeat (char *master);
#endif
}
+void Loop_Heartbeat (char *master)
+{
+}
+
+
void Loop_Listen (qboolean state)
{
}
}
+qboolean Loop_SearchForInetHosts (char *master)
+{
+ return false;
+}
+
+
qsocket_t *Loop_Connect (char *host)
{
if (strcmp(host,"local") != 0)
int Loop_Init (void);
void Loop_Listen (qboolean state);
void Loop_SearchForHosts (qboolean xmit);
+qboolean Loop_SearchForInetHosts (char *master);
qsocket_t *Loop_Connect (char *host);
qsocket_t *Loop_CheckNewConnections (void);
int Loop_GetMessage (qsocket_t *sock);
qboolean Loop_CanSendUnreliableMessage (qsocket_t *sock);
void Loop_Close (qsocket_t *sock);
void Loop_Shutdown (void);
+void Loop_Heartbeat (char *master);
#endif
// net_main.c
#include "quakedef.h"
+#include "net_master.h"
qsocket_t *net_activeSockets = NULL;
mempool_t *net_mempool;
PollProcedure slistSendProcedure = {NULL, 0.0, Slist_Send};
PollProcedure slistPollProcedure = {NULL, 0.0, Slist_Poll};
+static void InetSlist_Send(void);
+static void InetSlist_Poll(void);
+PollProcedure inetSlistSendProcedure = {NULL, 0.0, InetSlist_Send};
+PollProcedure inetSlistPollProcedure = {NULL, 0.0, InetSlist_Poll};
+
sizebuf_t net_message;
int net_activeconnections = 0;
}
+static void NET_Heartbeat_f (void)
+{
+ NET_Heartbeat ();
+}
+
+
static void PrintSlistHeader(void)
{
Con_Printf("Server Map Users\n");
}
+void NET_InetSlist_f (void)
+{
+ if (slistInProgress)
+ return;
+
+ if (! slistSilent)
+ {
+ Con_Printf("Looking for Quake servers...\n");
+ PrintSlistHeader();
+ }
+
+ slistInProgress = true;
+ slistStartTime = Sys_DoubleTime();
+
+ SchedulePollProcedure(&inetSlistSendProcedure, 0.0);
+ SchedulePollProcedure(&inetSlistPollProcedure, 0.1);
+
+ hostCacheCount = 0;
+}
+
+
static void Slist_Send(void)
{
for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
}
+static void InetSlist_Send(void)
+{
+ char* host;
+
+ if (!slistInProgress)
+ return;
+
+ while ((host = Master_BuildGetServers ()) != NULL)
+ {
+ for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
+ {
+ if (!slistLocal && net_driverlevel == 0)
+ continue;
+ if (net_drivers[net_driverlevel].initialized == false)
+ continue;
+ dfunc.SearchForInetHosts (host);
+ }
+ }
+
+ if ((Sys_DoubleTime() - slistStartTime) < 3.5)
+ SchedulePollProcedure(&inetSlistSendProcedure, 1.0);
+}
+
+
+static void InetSlist_Poll(void)
+{
+ for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
+ {
+ if (!slistLocal && net_driverlevel == 0)
+ continue;
+ if (net_drivers[net_driverlevel].initialized == false)
+ continue;
+ // We stop as soon as we have one answer (FIXME: bad...)
+ if (dfunc.SearchForInetHosts (NULL))
+ slistInProgress = false;
+ }
+
+ if (! slistSilent)
+ PrintSlist();
+
+ if (slistInProgress && (Sys_DoubleTime() - slistStartTime) < 4.0)
+ {
+ SchedulePollProcedure(&inetSlistPollProcedure, 0.1);
+ return;
+ }
+
+ if (! slistSilent)
+ PrintSlistTrailer();
+ slistInProgress = false;
+ slistSilent = false;
+ slistLocal = true;
+}
+
+
/*
===================
NET_Connect
}
+/*
+====================
+NET_Heartbeat
+
+Send an heartbeat to the master server(s)
+====================
+*/
+void NET_Heartbeat (void)
+{
+ char* host;
+ while ((host = Master_BuildHeartbeat ()) != NULL)
+ {
+ for (net_driverlevel=0 ; net_driverlevel<net_numdrivers; net_driverlevel++)
+ {
+ if (net_drivers[net_driverlevel].initialized == false)
+ continue;
+ if (net_driverlevel && listening == false)
+ continue;
+ dfunc.Heartbeat (host);
+ }
+ }
+}
+
+
int NET_SendToAll(sizebuf_t *data, int blocktime)
{
double start;
Cvar_RegisterVariable (&net_messagetimeout);
Cvar_RegisterVariable (&hostname);
- Cmd_AddCommand ("slist", NET_Slist_f);
+ Cmd_AddCommand ("net_slist", NET_Slist_f);
+ Cmd_AddCommand ("net_inetslist", NET_InetSlist_f);
Cmd_AddCommand ("listen", NET_Listen_f);
Cmd_AddCommand ("maxplayers", MaxPlayers_f);
Cmd_AddCommand ("port", NET_Port_f);
+ Cmd_AddCommand ("heartbeat", NET_Heartbeat_f);
// initialize all the drivers
for (net_driverlevel=0 ; net_driverlevel<net_numdrivers ; net_driverlevel++)
Con_DPrintf("IPX address %s\n", my_ipx_address);
if (*my_tcpip_address)
Con_DPrintf("TCP/IP address %s\n", my_tcpip_address);
+
+ Master_Init ();
}
/*
--- /dev/null
+/*
+Copyright (C) 2002 Mathieu Olivier
+
+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.
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// net_master.c
+
+#include <malloc.h>
+#include "quakedef.h"
+
+cvar_t sv_masters [] =
+{
+ {CVAR_SAVE, "sv_master1", ""},
+ {CVAR_SAVE, "sv_master2", ""},
+ {CVAR_SAVE, "sv_master3", ""},
+ {CVAR_SAVE, "sv_master4", ""}
+};
+
+
+/*
+====================
+Master_BuildGetServers
+
+Build a getservers request for a master server
+====================
+*/
+char* Master_BuildGetServers (void)
+{
+ static int nextmaster = 0;
+ cvar_t* sv_master;
+ char request [256];
+
+ if (nextmaster >= sizeof (sv_masters) / sizeof (sv_masters[0]))
+ {
+ nextmaster = 0;
+ return NULL;
+ }
+
+ sv_master = &sv_masters[nextmaster++];
+
+ // No master, no heartbeat
+ if (sv_master->string[0] == '\0')
+ {
+ nextmaster = 0;
+ return NULL;
+ }
+
+ // Build the heartbeat
+ snprintf (request, sizeof (request), "getservers %s %u empty full\x0A", gamename, NET_PROTOCOL_VERSION);
+ SZ_Clear (&net_message);
+ MSG_WriteLong (&net_message, -1);
+ MSG_WriteString (&net_message, request);
+
+ net_message.cursize--; // we don't send the trailing '\0'
+
+ return sv_master->string;
+}
+
+
+/*
+====================
+Master_BuildHeartbeat
+
+Build an heartbeat for a master server
+====================
+*/
+char* Master_BuildHeartbeat (void)
+{
+ static int nextmaster = 0;
+ cvar_t* sv_master;
+
+ if (nextmaster >= sizeof (sv_masters) / sizeof (sv_masters[0]))
+ {
+ nextmaster = 0;
+ return NULL;
+ }
+
+ sv_master = &sv_masters[nextmaster++];
+
+ // No master, no heartbeat
+ if (sv_master->string[0] == '\0')
+ {
+ nextmaster = 0;
+ return NULL;
+ }
+
+ // Build the heartbeat
+ SZ_Clear (&net_message);
+ MSG_WriteLong (&net_message, -1);
+ MSG_WriteString (&net_message, "heartbeat DarkPlaces\x0A");
+
+ net_message.cursize--; // we don't send the trailing '\0'
+
+ return sv_master->string;
+}
+
+
+/*
+====================
+Master_HandleMessage
+
+Handle the master server messages
+====================
+*/
+int Master_HandleMessage (void)
+{
+ const char* string = MSG_ReadString ();
+
+ // If it's a "getinfo" request
+ if (!strncmp (string, "getinfo", 7))
+ {
+ char response [512];
+ size_t length;
+
+ length = snprintf (response, sizeof (response), "infoResponse\x0A"
+ "\\gamename\\%s\\modname\\%s\\sv_maxclients\\%d"
+ "\\clients\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d",
+ gamename, com_modname, svs.maxclients, net_activeconnections,
+ sv.name, hostname.string, NET_PROTOCOL_VERSION);
+
+ // Too long to fit into the buffer?
+ if (length >= sizeof (response))
+ return -1;
+
+ // If there was a challenge in the getinfo message
+ if (string[7] == ' ')
+ {
+ string += 8; // skip the header and the space
+
+ // If the challenge wouldn't fit into the buffer
+ if (length + 11 + strlen (string) >= sizeof (response))
+ return -1;
+
+ sprintf (response + length, "\\challenge\\%s", string);
+ }
+
+ SZ_Clear (&net_message);
+ MSG_WriteLong (&net_message, -1);
+ MSG_WriteString (&net_message, response);
+
+ return net_message.cursize - 1;
+ }
+
+ return 0;
+}
+
+
+/*
+====================
+Master_Init
+
+Initialize the code that handles master server requests and reponses
+====================
+*/
+void Master_Init (void)
+{
+ unsigned int ind;
+ for (ind = 0; ind < sizeof (sv_masters) / sizeof (sv_masters[0]); ind++)
+ Cvar_RegisterVariable (&sv_masters[ind]);
+}
+
+
+/*
+====================
+Master_ParseServerList
+
+Parse getserverResponse messages
+====================
+*/
+void Master_ParseServerList (net_landriver_t* dfunc)
+{
+ int control;
+ qbyte* servers;
+ int ipaddr;
+ struct qsockaddr svaddr;
+ char ipstring [32];
+
+ if (net_message.cursize < sizeof(int))
+ return;
+
+ // is the cache full?
+ if (hostCacheCount == HOSTCACHESIZE)
+ return;
+
+ MSG_BeginReading ();
+ control = BigLong(*((int *)net_message.data));
+ MSG_ReadLong();
+ if (control != -1)
+ return;
+
+ if (strncmp (net_message.data + 4, "getserversResponse\\", 19))
+ return;
+
+ // Skip the next 18 bytes
+ MSG_ReadLong(); MSG_ReadLong(); MSG_ReadLong(); MSG_ReadLong();
+ MSG_ReadShort(); MSG_ReadByte();
+
+ servers = alloca (net_message.cursize - 23);
+ memcpy (servers , net_message.data + 23, net_message.cursize - 23);
+
+ // Extract the IP addresses
+ while ((ipaddr = (servers[3] << 24) + (servers[2] << 16) + (servers[1] << 8) + servers[0]) != -1)
+ {
+ int port = (servers[5] << 8) + servers[4];
+
+ if (port == -1 || port == 0)
+ break;
+
+ port = ((port >> 8) & 0xFF) + ((port & 0xFF) << 8);
+ sprintf (ipstring, "%u.%u.%u.%u:%hu",
+ ipaddr & 0xFF, (ipaddr >> 8) & 0xFF,
+ (ipaddr >> 16) & 0xFF, ipaddr >> 24,
+ port);
+ dfunc->GetAddrFromName (ipstring, &svaddr);
+
+ Con_DPrintf("Requesting info from server %s\n", ipstring);
+ // Send a request at this address
+ SZ_Clear(&net_message);
+ MSG_WriteLong(&net_message, 0); // save space for the header, filled in later
+ MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
+ MSG_WriteString(&net_message, "QUAKE");
+ MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ dfunc->Write(dfunc->controlSock, net_message.data, net_message.cursize, &svaddr);
+ SZ_Clear(&net_message);
+
+ if (servers[6] != '\\')
+ break;
+ servers += 7;
+ }
+}
--- /dev/null
+/*
+Copyright (C) 2002 Mathieu Olivier
+
+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.
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// net_master.h
+
+#ifndef NET_MASTER_H
+#define NET_MASTER_H
+
+#define MASTER_PORT 27950
+
+char* Master_BuildGetServers (void);
+char* Master_BuildHeartbeat (void);
+int Master_HandleMessage (void);
+void Master_Init (void);
+void Master_ParseServerList (net_landriver_t* dfunc);
+
+#endif
//=============================================================================
+int UDP_Recv (qbyte *buf, int len, struct qsockaddr *addr)
+{
+ return UDP_Read (net_acceptsocket, buf, len, addr);
+}
+
+//=============================================================================
+
+int UDP_Send (qbyte *buf, int len, struct qsockaddr *addr)
+{
+ return UDP_Write (net_acceptsocket, buf, len, addr);
+}
+
+//=============================================================================
+
int UDP_Read (int socket, qbyte *buf, int len, struct qsockaddr *addr)
{
int addrlen = sizeof (struct qsockaddr);
int UDP_CloseSocket (int socket);
int UDP_Connect (int socket, struct qsockaddr *addr);
int UDP_CheckNewConnections (void);
+int UDP_Recv (qbyte *buf, int len, struct qsockaddr *addr);
+int UDP_Send (qbyte *buf, int len, struct qsockaddr *addr);
int UDP_Read (int socket, qbyte *buf, int len, struct qsockaddr *addr);
int UDP_Write (int socket, qbyte *buf, int len, struct qsockaddr *addr);
int UDP_Broadcast (int socket, qbyte *buf, int len);
Loop_Init,
Loop_Listen,
Loop_SearchForHosts,
+ Loop_SearchForInetHosts,
Loop_Connect,
Loop_CheckNewConnections,
Loop_GetMessage,
Datagram_Init,
Datagram_Listen,
Datagram_SearchForHosts,
+ Datagram_SearchForInetHosts,
Datagram_Connect,
Datagram_CheckNewConnections,
Datagram_GetMessage,
WINS_CloseSocket,
WINS_Connect,
WINS_CheckNewConnections,
+ WINS_Recv,
+ WINS_Send,
WINS_Read,
WINS_Write,
WINS_Broadcast,
WIPX_CloseSocket,
WIPX_Connect,
WIPX_CheckNewConnections,
+ WIPX_Recv,
+ WIPX_Send,
WIPX_Read,
WIPX_Write,
WIPX_Broadcast,
//=============================================================================
+int WINS_Recv (qbyte *buf, int len, struct qsockaddr *addr)
+{
+ return WINS_Read (net_acceptsocket, buf, len, addr);
+}
+
+//=============================================================================
+
+int WINS_Send (qbyte *buf, int len, struct qsockaddr *addr)
+{
+ return WINS_Write (net_acceptsocket, buf, len, addr);
+}
+
+//=============================================================================
+
int WINS_Read (int socket, qbyte *buf, int len, struct qsockaddr *addr)
{
int addrlen = sizeof (struct qsockaddr);
int WINS_CloseSocket (int socket);
int WINS_Connect (int socket, struct qsockaddr *addr);
int WINS_CheckNewConnections (void);
+int WINS_Recv (qbyte *buf, int len, struct qsockaddr *addr);
+int WINS_Send (qbyte *buf, int len, struct qsockaddr *addr);
int WINS_Read (int socket, qbyte *buf, int len, struct qsockaddr *addr);
int WINS_Write (int socket, qbyte *buf, int len, struct qsockaddr *addr);
int WINS_Broadcast (int socket, qbyte *buf, int len);
//=============================================================================
+int WIPX_Recv (qbyte *buf, int len, struct qsockaddr *addr)
+{
+ return WIPX_Read (net_acceptsocket, buf, len, addr);
+}
+
+//=============================================================================
+
+int WIPX_Send (qbyte *buf, int len, struct qsockaddr *addr)
+{
+ return WIPX_Write (net_acceptsocket, buf, len, addr);
+}
+
+//=============================================================================
+
static qbyte packetBuffer[NET_DATAGRAMSIZE + 4];
int WIPX_Read (int handle, qbyte *buf, int len, struct qsockaddr *addr)
int WIPX_CloseSocket (int socket);
int WIPX_Connect (int socket, struct qsockaddr *addr);
int WIPX_CheckNewConnections (void);
+int WIPX_Recv (qbyte *buf, int len, struct qsockaddr *addr);
+int WIPX_Send (qbyte *buf, int len, struct qsockaddr *addr);
int WIPX_Read (int socket, qbyte *buf, int len, struct qsockaddr *addr);
int WIPX_Write (int socket, qbyte *buf, int len, struct qsockaddr *addr);
int WIPX_Broadcast (int socket, qbyte *buf, int len);
SV_ConnectClient (i);
net_activeconnections++;
+ NET_Heartbeat ();
}
}
SV_SendServerinfo (host_client);
Con_DPrintf ("Server spawned.\n");
+ NET_Heartbeat ();
}