cvar_t cl_anglespeedkey = {CVAR_SAVE, "cl_anglespeedkey","1.5"};
+cvar_t cl_nodelta = {0, "cl_nodelta", "0"};
/*
================
for (i = 0;i < 3;i++)
MSG_WriteFloat (&buf, cl.viewangles[i]);
}
- else if (dpprotocol == DPPROTOCOL_VERSION1)
+ else if (dpprotocol == DPPROTOCOL_VERSION1 || dpprotocol == DPPROTOCOL_VERSION4)
{
for (i=0 ; i<3 ; i++)
MSG_WritePreciseAngle (&buf, cl.viewangles[i]);
MSG_WriteByte (&buf, in_impulse);
in_impulse = 0;
- // LordHavoc: should we ack this on receipt instead? would waste net bandwidth though
- i = EntityFrame_MostRecentlyRecievedFrameNum(&cl.entitydatabase);
- if (i > 0)
+ if (dpprotocol == DPPROTOCOL_VERSION1 || dpprotocol == DPPROTOCOL_VERSION2 || dpprotocol == DPPROTOCOL_VERSION3)
{
- MSG_WriteByte(&buf, clc_ackentities);
- MSG_WriteLong(&buf, i);
+ // LordHavoc: should we ack this on receipt instead? would waste net bandwidth though
+ i = EntityFrame_MostRecentlyRecievedFrameNum(&cl.entitydatabase);
+ if (i > 0)
+ {
+ MSG_WriteByte(&buf, clc_ackentities);
+ MSG_WriteLong(&buf, i);
+ }
+ }
+ else
+ {
+ if (cl.entitydatabase4)
+ {
+ MSG_WriteByte(&buf, clc_ackentities);
+ if (cl_nodelta.integer)
+ MSG_WriteLong(&buf, -1);
+ else
+ MSG_WriteLong(&buf, cl.entitydatabase4->ackframenum);
+ }
}
// deliver the message
Cmd_AddCommand ("-button7", IN_Button7Up);
Cmd_AddCommand ("+button8", IN_Button8Down);
Cmd_AddCommand ("-button8", IN_Button8Up);
+
+ Cvar_RegisterVariable(&cl_nodelta);
}
if (!sv.active)
Host_ClearMemory ();
+ // note: this also gets rid of the entity database
Mem_EmptyPool(cl_entities_mempool);
// wipe the entire cl structure
// LordHavoc: lerp in listen games as the server is being capped below the client (usually)
f = cl.mtime[0] - cl.mtime[1];
- if (!f || cl_nolerp.integer || cls.timedemo || (sv.active && svs.maxclients == 1))
+ if (!f || cl_nolerp.integer || cls.timedemo || cl.islocalgame)
{
cl.time = cl.mtime[0];
return 1;
// parse protocol version number
i = MSG_ReadLong ();
- if (i != PROTOCOL_VERSION && i != DPPROTOCOL_VERSION1 && i != DPPROTOCOL_VERSION2 && i != DPPROTOCOL_VERSION3 && i != 250)
+ if (i != PROTOCOL_VERSION && i != DPPROTOCOL_VERSION1 && i != DPPROTOCOL_VERSION2 && i != DPPROTOCOL_VERSION3 && i != DPPROTOCOL_VERSION4 && i != 250)
{
- Host_Error ("Server is protocol %i, not %i, %i, %i or %i", i, DPPROTOCOL_VERSION1, DPPROTOCOL_VERSION2, DPPROTOCOL_VERSION3, PROTOCOL_VERSION);
+ Host_Error ("Server is protocol %i, not %i, %i, %i, %i or %i", i, DPPROTOCOL_VERSION1, DPPROTOCOL_VERSION2, DPPROTOCOL_VERSION3, DPPROTOCOL_VERSION4, PROTOCOL_VERSION);
return;
}
Nehahrademcompatibility = false;
if (cls.demoplayback && demo_nehahra.integer)
Nehahrademcompatibility = true;
dpprotocol = i;
- if (dpprotocol != DPPROTOCOL_VERSION1 && dpprotocol != DPPROTOCOL_VERSION2 && dpprotocol != DPPROTOCOL_VERSION3)
+ if (dpprotocol != DPPROTOCOL_VERSION1 && dpprotocol != DPPROTOCOL_VERSION2 && dpprotocol != DPPROTOCOL_VERSION3 && dpprotocol != DPPROTOCOL_VERSION4)
dpprotocol = 0;
// parse maxclients
// not a monster
ent->persistent.lerpstarttime = cl.mtime[1];
// no lerp if it's singleplayer
- if (sv.active && svs.maxclients == 1)
+ if (cl.islocalgame)
ent->persistent.lerpdeltatime = 0;
else
ent->persistent.lerpdeltatime = cl.mtime[0] - cl.mtime[1];
}
static entity_frame_t entityframe;
+extern mempool_t *cl_entities_mempool;
void CL_ReadEntityFrame(void)
{
- if (dpprotocol == DPPROTOCOL_VERSION3)
+ if (dpprotocol == DPPROTOCOL_VERSION1 || dpprotocol == DPPROTOCOL_VERSION2 || dpprotocol == DPPROTOCOL_VERSION3)
{
int i;
entity_t *ent;
}
}
else
- EntityFrame4_CL_ReadFrame(&cl.entitydatabase4);
+ {
+ if (!cl.entitydatabase4)
+ cl.entitydatabase4 = EntityFrame4_AllocDatabase(cl_entities_mempool);
+ EntityFrame4_CL_ReadFrame(cl.entitydatabase4);
+ }
}
void CL_EntityUpdateSetup(void)
void CL_EntityUpdateEnd(void)
{
- if (dpprotocol != DPPROTOCOL_VERSION4)
+ if (dpprotocol == PROTOCOL_VERSION || dpprotocol == DPPROTOCOL_VERSION1 || dpprotocol == DPPROTOCOL_VERSION2 || dpprotocol == DPPROTOCOL_VERSION3)
{
int i;
// disable entities that disappeared this frame
{
int i;
- memset(&ent->state_baseline, 0, sizeof(entity_state_t));
+ ClearStateToDefault(&ent->state_baseline);
ent->state_baseline.active = true;
if (large)
{
ent->state_baseline.origin[i] = MSG_ReadCoord ();
ent->state_baseline.angles[i] = MSG_ReadAngle ();
}
- ent->state_baseline.alpha = 255;
- ent->state_baseline.scale = 16;
- ent->state_baseline.glowsize = 0;
- ent->state_baseline.glowcolor = 254;
- ent->state_previous = ent->state_current = ent->state_baseline;
-
CL_ValidateState(&ent->state_baseline);
+ ent->state_previous = ent->state_current = ent->state_baseline;
}
case svc_version:
i = MSG_ReadLong ();
- if (i != PROTOCOL_VERSION && i != DPPROTOCOL_VERSION1 && i != DPPROTOCOL_VERSION2 && i != DPPROTOCOL_VERSION3 && i != 250)
- Host_Error ("CL_ParseServerMessage: Server is protocol %i, not %i, %i, %i or %i", i, DPPROTOCOL_VERSION1, DPPROTOCOL_VERSION2, DPPROTOCOL_VERSION3, PROTOCOL_VERSION);
+ if (i != PROTOCOL_VERSION && i != DPPROTOCOL_VERSION1 && i != DPPROTOCOL_VERSION2 && i != DPPROTOCOL_VERSION3 && i != DPPROTOCOL_VERSION4 && i != 250)
+ Host_Error ("CL_ParseServerMessage: Server is protocol %i, not %i, %i, %i, %i or %i", i, DPPROTOCOL_VERSION1, DPPROTOCOL_VERSION2, DPPROTOCOL_VERSION3, DPPROTOCOL_VERSION4, PROTOCOL_VERSION);
Nehahrademcompatibility = false;
if (i == 250)
Nehahrademcompatibility = true;
if (cls.demoplayback && demo_nehahra.integer)
Nehahrademcompatibility = true;
dpprotocol = i;
- if (dpprotocol != DPPROTOCOL_VERSION1 && dpprotocol != DPPROTOCOL_VERSION2 && dpprotocol != DPPROTOCOL_VERSION3)
+ if (dpprotocol != DPPROTOCOL_VERSION1 && dpprotocol != DPPROTOCOL_VERSION2 && dpprotocol != DPPROTOCOL_VERSION3 && dpprotocol != DPPROTOCOL_VERSION4)
dpprotocol = 0;
break;
//
typedef struct
{
+ // true if playing in a local game and no one else is connected
+ int islocalgame;
+
// when connecting to the server throw out the first couple move messages
// so the player doesn't accidentally do something the first frame
int movemessages;
// entity database stuff
entity_database_t entitydatabase;
- entity_database4_t entitydatabase4;
+ entity_database4_t *entitydatabase4;
}
client_state_t;
// used by client
float MSG_ReadCoord (void)
{
- if (dpprotocol == DPPROTOCOL_VERSION2 || dpprotocol == DPPROTOCOL_VERSION3)
+ if (dpprotocol == DPPROTOCOL_VERSION2 || dpprotocol == DPPROTOCOL_VERSION3 || dpprotocol == DPPROTOCOL_VERSION4)
return (signed short) MSG_ReadLittleShort();
else if (dpprotocol == DPPROTOCOL_VERSION1)
return MSG_ReadLittleFloat();
static void R_SetupFrame (void)
{
// don't allow cheats in multiplayer
- if (cl.maxclients > 1)
+ if (!cl.islocalgame)
{
if (r_fullbright.integer != 0)
Cvar_Set ("r_fullbright", "0");
{
int i, numplayers;
+ // general default
+ numplayers = 8;
+
if (cl_available)
{
// client exists, check what mode the user wants
if (i)
{
cls.state = ca_dedicated;
- numplayers = 8;
+ // default players unless specified
if (i != (com_argc - 1))
numplayers = atoi (com_argv[i+1]);
if (COM_CheckParm ("-listen"))
}
else
{
- numplayers = 1;
cls.state = ca_disconnected;
i = COM_CheckParm ("-listen");
if (i)
{
- numplayers = 8;
+ // default players unless specified
if (i != (com_argc - 1))
numplayers = atoi (com_argv[i+1]);
}
+ else
+ {
+ // default players in some games, singleplayer in most
+ if (gamemode != GAME_TRANSFUSION && gamemode != GAME_GOODVSBAD2 && gamemode != GAME_NEXUIZ || gamemode == GAME_BATTLEMECH)
+ numplayers = 1;
+ }
}
}
else
{
- // no client in the executable, start dedicated server
+ // no client in the executable, always start dedicated server
if (COM_CheckParm ("-listen"))
Sys_Error ("-listen not available in a dedicated server executable");
- numplayers = 8;
cls.state = ca_dedicated;
// check for -dedicated specifying how many players
i = COM_CheckParm ("-dedicated");
+ // default players unless specified
if (i && i != (com_argc - 1))
numplayers = atoi (com_argv[i+1]);
}
if (numplayers < 1)
numplayers = 8;
- if (numplayers > MAX_SCOREBOARD)
- numplayers = MAX_SCOREBOARD;
- // Transfusion doesn't support single player games
- if (gamemode == GAME_TRANSFUSION && numplayers < 4)
- numplayers = 4;
+ numplayers = bound(1, numplayers, MAX_SCOREBOARD);
if (numplayers > 1)
- Cvar_SetValueQuick (&deathmatch, 1);
+ {
+ if (!deathmatch.integer)
+ Cvar_SetValueQuick(&deathmatch, 1);
+ }
else
- Cvar_SetValueQuick (&deathmatch, 0);
+ Cvar_SetValueQuick(&deathmatch, 0);
- svs.maxclients = 0;
- SV_SetMaxClients(numplayers);
-}
-
-static mempool_t *clients_mempool;
-void SV_SetMaxClients(int n)
-{
- if (sv.active)
- return;
- n = bound(1, n, MAX_SCOREBOARD);
- if (svs.maxclients == n)
- return;
- svs.maxclients = n;
- if (!clients_mempool)
- clients_mempool = Mem_AllocPool("clients");
- if (svs.clients)
- Mem_Free(svs.clients);
- svs.clients = Mem_Alloc(clients_mempool, svs.maxclients*sizeof(client_t));
+ Cvar_SetValueQuick(&sv_maxplayers, numplayers);
}
-
/*
=======================
Host_InitLocal
va_list argptr;
char string[4096];
int i;
+ client_t *client;
va_start(argptr,fmt);
vsnprintf(string, sizeof(string), fmt,argptr);
va_end(argptr);
- for (i=0 ; i<svs.maxclients ; i++)
- if (svs.clients[i].active && svs.clients[i].spawned)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
+ {
+ if ((client = svs.connectedclients[i]) && client->spawned)
{
- MSG_WriteByte(&svs.clients[i].message, svc_print);
- MSG_WriteString(&svs.clients[i].message, string);
+ MSG_WriteByte(&client->message, svc_print);
+ MSG_WriteString(&client->message, string);
}
+ }
if (sv_echobprint.integer && cls.state == ca_dedicated)
Sys_Printf("%s", string);
if (host_client->netconnection)
{
// free the client (the body stays around)
- host_client->active = false;
-
if (!crash)
{
// LordHavoc: no opportunity for resending, so use unreliable
}
}
- // now clear name (after ClientDisconnect was called)
- host_client->name[0] = 0;
- host_client->old_frags = -999999;
-
// send notification to all clients
- for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
{
- if (!client->active)
+ if (!(client = svs.connectedclients[i]))
continue;
MSG_WriteByte(&client->message, svc_updatename);
- MSG_WriteByte(&client->message, host_client - svs.clients);
+ MSG_WriteByte(&client->message, host_client->number);
MSG_WriteString(&client->message, "");
MSG_WriteByte(&client->message, svc_updatefrags);
- MSG_WriteByte(&client->message, host_client - svs.clients);
+ MSG_WriteByte(&client->message, host_client->number);
MSG_WriteShort(&client->message, 0);
MSG_WriteByte(&client->message, svc_updatecolors);
- MSG_WriteByte(&client->message, host_client - svs.clients);
+ MSG_WriteByte(&client->message, host_client->number);
MSG_WriteByte(&client->message, 0);
}
NetConn_Heartbeat(1);
+
+ // free the client now
+ if (host_client->entitydatabase4)
+ EntityFrame4_FreeDatabase(host_client->entitydatabase4);
+ // remove the index reference
+ svs.connectedclients[host_client->number] = NULL;
+ Mem_Free(host_client);
}
/*
count = 0;
NetConn_ClientFrame();
NetConn_ServerFrame();
- for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
{
- if (host_client->active && host_client->message.cursize)
+ host_client = svs.connectedclients[i];
+ if (host_client && host_client->message.cursize)
{
if (NetConn_CanSendMessage(host_client->netconnection))
{
if (count)
Con_Printf("Host_ShutdownServer: NetConn_SendToAll failed for %u clients\n", count);
- for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
- if (host_client->active)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
+ if ((host_client = svs.connectedclients[i]))
SV_DropClient(crash); // server shutdown
NetConn_CloseServerPorts();
// clear structures
//
memset(&sv, 0, sizeof(sv));
- memset(svs.clients, 0, svs.maxclients * sizeof(client_t));
}
{
static double frametimetotal = 0, lastservertime = 0;
frametimetotal += host_frametime;
- // LordHavoc: cap server at sys_ticrate in listen games
- if (cls.state != ca_dedicated && svs.maxclients > 1 && ((realtime - lastservertime) < sys_ticrate.value))
+ // LordHavoc: cap server at sys_ticrate in networked games
+ if (!cl.islocalgame && ((realtime - lastservertime) < sys_ticrate.value))
return;
NetConn_ServerFrame();
// run the world state
- if (!sv.paused && (svs.maxclients > 1 || (key_dest == key_game && !key_consoleactive)))
+ if (!sv.paused && (!cl.islocalgame || (key_dest == key_game && !key_consoleactive)))
sv.frametime = pr_global_struct->frametime = frametimetotal;
else
sv.frametime = 0;
return;
}
+ cl.islocalgame = NetConn_IsLocalGame();
+
// get new key events
Sys_SendKeyEvents();
timecount = 0;
timetotal = 0;
c = 0;
- for (i=0 ; i<svs.maxclients ; i++)
- {
- if (svs.clients[i].active)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
+ if (svs.connectedclients[i])
c++;
- }
Con_Printf ("serverprofile: %2i clients %2i msec\n", c, m);
}
else
print = SV_ClientPrintf;
- for (players = 0, j = 0;j < svs.maxclients;j++)
- if (svs.clients[j].active)
+ for (players = 0, j = 0;j < MAX_SCOREBOARD;j++)
+ if (svs.connectedclients[j])
players++;
print ("host: %s\n", Cvar_VariableString ("hostname"));
print ("version: %s build %s\n", gamename, buildstring);
print ("map: %s\n", sv.name);
- print ("players: %i active (%i max)\n\n", players, svs.maxclients);
- for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
+ print ("players: %i active (%i max)\n\n", players, min(sv_maxplayers.integer, MAX_SCOREBOARD));
+ for (j = 0;j < MAX_SCOREBOARD;j++)
{
- if (!client->active)
+ if (!(client = svs.connectedclients[j]))
continue;
seconds = (int)(realtime - client->netconnection->connecttime);
minutes = seconds / 60;
}
SV_ClientPrintf ("Client ping times:\n");
- for (i=0, client = svs.clients ; i<svs.maxclients ; i++, client++)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
{
- if (!client->active)
+ if (!(client = svs.connectedclients[i]))
continue;
total = 0;
for (j=0 ; j<NUM_PING_TIMES ; j++)
return;
}
- if (svs.maxclients != 1)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
{
- Con_Printf ("Can't save multiplayer games.\n");
- return;
+ if (svs.connectedclients[i])
+ {
+ if (i > 0)
+ {
+ Con_Printf("Can't save multiplayer games.\n");
+ return;
+ }
+ if (svs.connectedclients[i]->edict->v->deadflag)
+ {
+ Con_Printf("Can't savegame with a dead player\n");
+ return;
+ }
+ }
}
if (Cmd_Argc() != 2)
return;
}
- for (i=0 ; i<svs.maxclients ; i++)
- {
- if (svs.clients[i].active && (svs.clients[i].edict->v->health <= 0) )
- {
- Con_Printf ("Can't savegame with a dead player\n");
- return;
- }
- }
-
strncpy (name, Cmd_Argv(1), sizeof (name) - 1);
name[sizeof (name) - 1] = '\0';
FS_DefaultExtension (name, ".sav");
Host_SavegameComment (comment);
FS_Printf (f, "%s\n", comment);
for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
- FS_Printf (f, "%f\n", svs.clients->spawn_parms[i]);
+ FS_Printf (f, "%f\n", svs.connectedclients[0]->spawn_parms[i]);
FS_Printf (f, "%d\n", current_skill);
FS_Printf (f, "%s\n", sv.name);
FS_Printf (f, "%f\n",sv.time);
FS_Close (f);
for (i = 0;i < NUM_SPAWN_PARMS;i++)
- svs.clients->spawn_parms[i] = spawn_parms[i];
+ svs.connectedclients[0]->spawn_parms[i] = spawn_parms[i];
// make sure we're connected to loopback
if (cls.state == ca_disconnected || !(cls.state == ca_connected && cls.netcon != NULL && LHNETADDRESS_GetAddressType(&cls.netcon->peeraddress) == LHNETADDRESSTYPE_LOOP))
// send notification to all clients
MSG_WriteByte(&sv.reliable_datagram, svc_updatename);
- MSG_WriteByte(&sv.reliable_datagram, host_client - svs.clients);
+ MSG_WriteByte(&sv.reliable_datagram, host_client->number);
MSG_WriteString(&sv.reliable_datagram, host_client->name);
}
text[j++] = '\n';
text[j++] = 0;
- for (j = 0, host_client = svs.clients; j < svs.maxclients; j++, host_client++)
- if (host_client && host_client->active && host_client->spawned && (!teamplay.integer || host_client->edict->v->team == save->edict->v->team))
+ for (j = 0;j < MAX_SCOREBOARD;j++)
+ if ((host_client = svs.connectedclients[j]) && host_client->spawned && (!teamplay.integer || host_client->edict->v->team == save->edict->v->team))
SV_ClientPrintf("%s", text);
host_client = save;
text[j++] = 0;
save = host_client;
- for (j = 0, host_client = svs.clients; j < svs.maxclients; j++, host_client++)
- {
- if (host_client->active && host_client->spawned && !strcasecmp(host_client->name, Cmd_Argv(1)))
- {
+ for (j = 0;j < MAX_SCOREBOARD;j++)
+ if ((host_client = svs.connectedclients[j]) && host_client->spawned && !strcasecmp(host_client->name, Cmd_Argv(1)))
SV_ClientPrintf("%s", text);
- break;
- }
- }
host_client = save;
}
// send notification to all clients
MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
- MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
+ MSG_WriteByte (&sv.reliable_datagram, host_client->number);
MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
}
}
MSG_WriteByte (&host_client->message, svc_time);
MSG_WriteFloat (&host_client->message, sv.time);
- for (i=0, client = svs.clients ; i<svs.maxclients ; i++, client++)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
{
+ if (!(client = svs.connectedclients[i]))
+ continue;
MSG_WriteByte (&host_client->message, svc_updatename);
MSG_WriteByte (&host_client->message, i);
MSG_WriteString (&host_client->message, client->old_name);
if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
{
i = atof(Cmd_Argv(2)) - 1;
- if (i < 0 || i >= svs.maxclients)
+ if (i < 0 || i >= MAX_SCOREBOARD || !(host_client = svs.connectedclients[i]))
return;
- if (!svs.clients[i].active)
- return;
- host_client = &svs.clients[i];
byNumber = true;
}
else
{
- for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
{
- if (!host_client->active)
+ if (!(host_client = svs.connectedclients[i]))
continue;
if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
break;
}
}
- if (i < svs.maxclients)
+ if (i < MAX_SCOREBOARD)
{
if (cmd_source == src_command)
{
{
int i, c;
- if (cls.state == ca_dedicated)
+ if (cls.state == ca_dedicated || sv_maxplayers.integer > 1)
{
if (!sv.active && !sv_spawnmap[0])
{
return;
if (cl.intermission)
return;
- if (svs.maxclients != 1)
+ if (!cl.islocalgame)
return;
m_entersound = true;
m_state = m_save;
m_state = m_gameoptions;
m_entersound = true;
if (maxplayers == 0)
- maxplayers = svs.maxclients;
+ maxplayers = sv_maxplayers.integer;
if (maxplayers < 2)
maxplayers = MAX_SCOREBOARD;
}
#define MASTER_PORT 27950
+cvar_t sv_maxplayers = {0, "maxplayers", "8"};
cvar_t sv_public = {0, "sv_public", "0"};
static cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "180"};
Host_Reconnect_f();
}
+int NetConn_IsLocalGame(void)
+{
+ int i;
+ if (cls.state == ca_connected && sv.active/* && LHNETADDRESS_GetAddressType(cl.netcon->peeraddress) == LHNETADDRESSTYPE_LOOP*/)
+ {
+ // make sure there are no other connected clients
+ for (i = 1;i < MAX_SCOREBOARD;i++)
+ if (svs.connectedclients[i])
+ return false;
+ return true;
+ }
+ return false;
+}
+
static struct
{
double senttime;
else
{
// see if this is a duplicate connection request
- for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
- if (client->active && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
+ for (clientnum = 0;clientnum < MAX_SCOREBOARD;clientnum++)
+ if ((client = svs.connectedclients[clientnum]) && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
break;
- if (clientnum < svs.maxclients)
+ if (clientnum < MAX_SCOREBOARD)
{
// duplicate connection request
if (realtime - client->netconnection->connecttime < 2.0)
else
{
// this is a new client, find a slot
- for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
- if (!client->active)
+ for (clientcount = 0, clientnum = 0;clientnum < MAX_SCOREBOARD;clientnum++)
+ if (svs.connectedclients[clientnum])
+ clientcount++;
+ for (clientnum = 0;clientnum < MAX_SCOREBOARD;clientnum++)
+ if (!svs.connectedclients[clientnum])
break;
- if (clientnum == svs.maxclients)
+ if (clientcount < max(1, sv_maxplayers.integer) && clientnum < MAX_SCOREBOARD)
+ {
+ // allocate and prepare the client struct
+ if ((client = Mem_Alloc(sv_clients_mempool, sizeof(client_t))))
+ {
+ if ((client->entitydatabase4 = EntityFrame4_AllocDatabase(sv_clients_mempool)))
+ {
+ if ((conn = NetConn_Open(mysocket, peeraddress)))
+ {
+ // allocated connection
+ LHNETADDRESS_ToString(peeraddress, conn->address, sizeof(conn->address), true);
+ if (developer.integer)
+ Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
+ NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
+ // now set up the client struct
+ svs.connectedclients[clientnum] = client;
+ SV_ConnectClient(clientnum, conn);
+ NetConn_Heartbeat(1);
+ }
+ else
+ {
+ EntityFrame4_FreeDatabase(client->entitydatabase4);
+ Mem_Free(client);
+ }
+ }
+ else
+ Mem_Free(client);
+ }
+ }
+ else
{
// server is full
if (developer.integer)
Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
}
- else
- {
- if ((conn = NetConn_Open(mysocket, peeraddress)))
- {
- // allocated connection
- LHNETADDRESS_ToString(peeraddress, conn->address, sizeof(conn->address), true);
- if (developer.integer)
- Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
- NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
- // now set up the client struct
- SV_ConnectClient(clientnum, conn);
- NetConn_Heartbeat(1);
- }
- }
}
}
}
// If there was a challenge in the getinfo message
if (length > 8 && string[7] == ' ')
challenge = string + 8;
- for (i = 0, n = 0;i < svs.maxclients;i++)
- if (svs.clients[i].active)
+ for (i = 0, n = 0;i < MAX_SCOREBOARD;i++)
+ if (svs.connectedclients[i])
n++;
responselength = snprintf(response, sizeof(response), "\377\377\377\377infoResponse\x0A"
"\\gamename\\%s\\modname\\%s\\sv_maxclients\\%d"
"\\clients\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d%s%s",
- gamename, com_modname, svs.maxclients, n,
+ gamename, com_modname, min(sv_maxplayers.integer, MAX_SCOREBOARD), n,
sv.name, hostname.string, NET_PROTOCOL_VERSION, challenge ? "\\challenge\\" : "", challenge ? challenge : "");
// does it fit in the buffer?
if (responselength < (int)sizeof(response))
else
{
// see if this is a duplicate connection request
- for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
- if (client->active && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
+ for (clientnum = 0;clientnum < MAX_SCOREBOARD;clientnum++)
+ if ((client = svs.connectedclients[clientnum]) && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
break;
- if (clientnum < svs.maxclients)
+ if (clientnum < MAX_SCOREBOARD)
{
// duplicate connection request
if (realtime - client->netconnection->connecttime < 2.0)
else
{
// this is a new client, find a slot
- for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
- if (!client->active)
+ for (clientnum = 0;clientnum < MAX_SCOREBOARD;clientnum++)
+ if (!(client = svs.connectedclients[clientnum]))
break;
- if (clientnum < svs.maxclients && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
+ // WARNING: this is broken code
+ if (clientnum < MAX_SCOREBOARD && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
{
// connect to the client
// everything is allocated, just fill in the details
MSG_WriteString(&net_message, hostname.string);
MSG_WriteString(&net_message, sv.name);
MSG_WriteByte(&net_message, net_activeconnections);
- MSG_WriteByte(&net_message, svs.maxclients);
+ MSG_WriteByte(&net_message, min(sv_maxplayers.integer, MAX_SCOREBOARD));
MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
}
}
#endif
- for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
{
- if (host_client->active && host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
+ if ((host_client = svs.connectedclients[i]))
{
- sv_player = host_client->edict;
- if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length)) == 2)
- SV_ReadClientMessage();
- return ret;
+ if (host_client->netconnection)
+ {
+ if (host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
+ {
+ sv_player = host_client->edict;
+ if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length)) == 2)
+ SV_ReadClientMessage();
+ return ret;
+ }
+ }
+ else
+ {
+ Con_Printf("Removing client with no netconnection!\n");
+ SV_DropClient(true);
+ }
}
}
}
for (i = 0;i < sv_numsockets;i++)
while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
- for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
{
- if (host_client->active && realtime > host_client->netconnection->timeout)
+ if ((host_client = svs.connectedclients[i]) && realtime > host_client->netconnection->timeout)
{
Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
sv_player = host_client->edict;
// make advertising optional and don't advertise singleplayer games, and
// only send a heartbeat as often as the admin wants
- if (sv.active && sv_public.integer && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
+ if (sv.active && sv_public.integer && (!cl.islocalgame || sv_maxplayers.integer >= 2) && (priority > 1 || realtime > nextheartbeattime))
{
nextheartbeattime = realtime + sv_heartbeatperiod.value;
for (masternum = 0;sv_masters[masternum].name;masternum++)
count = 0;
NetConn_ClientFrame();
NetConn_ServerFrame();
- for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
{
- if (host_client->active)
+ if ((host_client = svs.connectedclients[i]))
{
if (NetConn_CanSendMessage(host_client->netconnection))
{
return count;
}
-static void MaxPlayers_f(void)
-{
- int n;
-
- if (Cmd_Argc() != 2)
- {
- Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients);
- return;
- }
-
- if (sv.active)
- {
- Con_Printf("maxplayers can not be changed while a server is running.\n");
- return;
- }
-
- n = atoi(Cmd_Argv(1));
- n = bound(1, n, MAX_SCOREBOARD);
- if (svs.maxclients != n)
- Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
-
- SV_SetMaxClients(n);
-}
-
static void Net_Heartbeat_f(void)
{
NetConn_Heartbeat(2);
netconn_mempool = Mem_AllocPool("Networking");
Cmd_AddCommand("net_stats", Net_Stats_f);
Cmd_AddCommand("net_slist", Net_Slist_f);
- Cmd_AddCommand("maxplayers", MaxPlayers_f);
Cmd_AddCommand("heartbeat", Net_Heartbeat_f);
Cvar_RegisterVariable(&net_messagetimeout);
Cvar_RegisterVariable(&net_messagerejointimeout);
Cvar_RegisterVariable(&sv_netport);
Cvar_RegisterVariable(&sv_netaddress);
Cvar_RegisterVariable(&sv_netaddress_ipv6);
+ Cvar_RegisterVariable(&sv_maxplayers);
Cvar_RegisterVariable(&sv_public);
Cvar_RegisterVariable(&sv_heartbeatperiod);
for (masternum = 0;sv_masters[masternum].name;masternum++)
//
//============================================================================
+extern cvar_t sv_maxplayers;
extern sizebuf_t net_message;
int NetConn_SendReliableMessage(netconn_t *conn, sizebuf_t *data);
netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress);
void NetConn_Close(netconn_t *conn);
void NetConn_Listen(qboolean state);
+int NetConn_IsLocalGame(void);
//int NetConn_ReceivedMessage(netconn_t *conn, qbyte *data, int length);
//int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, lhnetaddress_t *peeraddress);
//int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, lhnetaddress_t *peeraddress);
entnum = G_EDICTNUM(OFS_PARM0);
s = PF_VarString(1);
- if (entnum < 1 || entnum > svs.maxclients)
+ if (entnum < 1 || entnum > MAX_SCOREBOARD || !svs.connectedclients[entnum-1])
{
Con_Printf ("tried to sprint to a non-client\n");
return;
}
- client = &svs.clients[entnum-1];
-
- MSG_WriteChar (&client->message,svc_print);
- MSG_WriteString (&client->message, s );
+ client = svs.connectedclients[entnum-1];
+ MSG_WriteChar(&client->message,svc_print);
+ MSG_WriteString(&client->message, s );
}
entnum = G_EDICTNUM(OFS_PARM0);
s = PF_VarString(1);
- if (entnum < 1 || entnum > svs.maxclients)
+ if (entnum < 1 || entnum > MAX_SCOREBOARD || !svs.connectedclients[entnum-1])
{
Con_Printf ("tried to sprint to a non-client\n");
return;
}
- client = &svs.clients[entnum-1];
-
- MSG_WriteChar (&client->message,svc_centerprint);
- MSG_WriteString (&client->message, s );
+ client = svs.connectedclients[entnum-1];
+ MSG_WriteChar(&client->message,svc_centerprint);
+ MSG_WriteString(&client->message, s );
}
// cycle to the next one
- if (check < 1)
- check = 1;
- if (check > svs.maxclients)
- check = svs.maxclients;
-
- if (check == svs.maxclients)
+ check = bound(1, check, MAX_SCOREBOARD);
+ if (check == MAX_SCOREBOARD)
i = 1;
else
i = check + 1;
for ( ; ; i++)
{
+ // count the cost
pr_xfunction->builtinsprofile++;
- if (i == svs.maxclients+1)
+ // wrap around
+ if (i == MAX_SCOREBOARD+1)
i = 1;
-
+ // look up the client's edict
ent = EDICT_NUM(i);
-
- if (i == check)
- break; // didn't find anything else
-
- if (ent->e->free)
+ // check if it is to be ignored, but never ignore the one we started on (prevent infinite loop)
+ if (i != check && (ent->e->free || ent->v->health <= 0 || ((int)ent->v->flags & FL_NOTARGET)))
continue;
- if (ent->v->health <= 0)
- continue;
- if ((int)ent->v->flags & FL_NOTARGET)
- continue;
-
- // anything that is a client, or has a client as an enemy
+ // found a valid client (possibly the same one again)
break;
}
client_t *old;
entnum = G_EDICTNUM(OFS_PARM0);
- if (entnum < 1 || entnum > svs.maxclients)
+ if (entnum < 1 || entnum > MAX_SCOREBOARD)
Host_Error ("Parm 0 not a client");
str = G_STRING(OFS_PARM1);
old = host_client;
- host_client = &svs.clients[entnum-1];
- Host_ClientCommands ("%s", str);
+ if ((host_client = svs.connectedclients[entnum-1]))
+ Host_ClientCommands ("%s", str);
host_client = old;
}
ed = G_EDICT(OFS_PARM0);
if (ed == sv.edicts)
Host_Error("remove: tried to remove world\n");
- if (NUM_FOR_EDICT(ed) <= svs.maxclients)
+ if (NUM_FOR_EDICT(ed) <= MAX_SCOREBOARD)
Host_Error("remove: tried to remove a client\n");
ED_Free (ed);
}
if (sv.state != ss_active)
return;
- for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
- if (client->active || client->spawned)
+ for (j = 0;j < MAX_SCOREBOARD;j++)
+ {
+ if ((client = svs.connectedclients[j]))
{
MSG_WriteChar (&client->message, svc_lightstyle);
MSG_WriteChar (&client->message,style);
MSG_WriteString (&client->message, val);
}
+ }
}
void PF_rint (void)
case MSG_ONE:
ent = PROG_TO_EDICT(pr_global_struct->msg_entity);
entnum = NUM_FOR_EDICT(ent);
- if (entnum < 1 || entnum > svs.maxclients)
- Host_Error ("WriteDest: not a client");
- return &svs.clients[entnum-1].message;
+ if (entnum < 1 || entnum > MAX_SCOREBOARD || svs.connectedclients[entnum-1] == NULL)
+ Host_Error("WriteDest: not a client");
+ return &svs.connectedclients[entnum-1]->message;
case MSG_ALL:
return &sv.reliable_datagram;
ent = G_EDICT(OFS_PARM0);
i = NUM_FOR_EDICT(ent);
- if (i < 1 || i > svs.maxclients)
+ if (i < 1 || i > MAX_SCOREBOARD || !(client = svs.connectedclients[i-1]))
Host_Error ("Entity is not a client");
// copy spawn parms out of the client_t
- client = svs.clients + (i-1);
-
for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
(&pr_global_struct->parm1)[i] = client->spawn_parms[i];
}
entnum = G_EDICTNUM(OFS_PARM0);
i = G_FLOAT(OFS_PARM1);
- if (entnum < 1 || entnum > svs.maxclients)
+ if (entnum < 1 || entnum > MAX_SCOREBOARD || !(client = svs.connectedclients[entnum-1]))
{
Con_Printf ("tried to setcolor a non-client\n");
return;
}
- client = &svs.clients[entnum-1];
if ((val = GETEDICTFIELDVALUE(client->edict, eval_clientcolors)))
val->_float = i;
client->colors = i;
//string(string s, float start, float length) substring = #116; // returns a section of a string as a tempstring
void PF_substring(void)
{
- int i, start, end;
- char *s, string[MAX_VARSTRING];
+ int i, start, length;
+ char *s, *string = PR_GetTempString();
s = G_STRING(OFS_PARM0);
start = G_FLOAT(OFS_PARM1);
- end = G_FLOAT(OFS_PARM2) + start;
+ length = G_FLOAT(OFS_PARM2);
if (!s)
s = "";
for (i = 0;i < start && *s;i++, s++);
- for (i = 0;i < MAX_VARSTRING - 1 && *s && i < end;i++, s++)
+ for (i = 0;i < STRINGTEMP_LENGTH - 1 && *s && i < length;i++, s++)
string[i] = *s;
string[i] = 0;
G_INT(OFS_RETURN) = PR_SetString(string);
//find client for this entity
i = (NUM_FOR_EDICT(G_EDICT(OFS_PARM0)) - 1);
- if (i < 0 || i >= svs.maxclients)
+ if (i < 0 || i >= MAX_SCOREBOARD || !svs.connectedclients[i])
Host_Error("PF_clientcommand: entity is not a client");
temp_client = host_client;
- host_client = &svs.clients[i];
+ host_client = svs.connectedclients[i];
Cmd_ExecuteString (G_STRING(OFS_PARM1), src_client);
host_client = temp_client;
}
if (modelindex >= 0 && modelindex < MAX_MODELS)
{
model = sv.models[modelindex];
- if (model->data_overridetagnamesforskin && (unsigned int)tagentity->v->skin < model->numskins && model->data_overridetagnamesforskin[(unsigned int)tagentity->v->skin].num_overridetagnames)
+ if (model->data_overridetagnamesforskin && (unsigned int)tagentity->v->skin < (unsigned int)model->numskins && model->data_overridetagnamesforskin[(unsigned int)tagentity->v->skin].num_overridetagnames)
for (i = 0;i < model->data_overridetagnamesforskin[(unsigned int)tagentity->v->skin].num_overridetagnames;i++)
if (!strcmp(tagname, model->data_overridetagnamesforskin[(unsigned int)tagentity->v->skin].data_overridetagnames[i].name))
v->_float = i + 1;
e->e->free = false;
// LordHavoc: for consistency set these here
num = NUM_FOR_EDICT(e) - 1;
- if (num >= 0 && num < svs.maxclients)
+ if (num >= 0 && num < MAX_SCOREBOARD && svs.connectedclients[num])
{
e->v->colormap = num + 1;
- e->v->team = (svs.clients[num].colors & 15) + 1;
- e->v->netname = PR_SetString(svs.clients[num].name);
+ e->v->team = (svs.connectedclients[num]->colors & 15) + 1;
+ e->v->netname = PR_SetString(svs.connectedclients[num]->name);
}
}
int i;
edict_t *e;
- for ( i=svs.maxclients+1 ; i<sv.num_edicts ; i++)
+ for (i = MAX_SCOREBOARD + 1;i < sv.num_edicts;i++)
{
e = EDICT_NUM(i);
// the first couple seconds of server time can involve a lot of
#include "quakedef.h"
+entity_state_t defaultstate =
+{
+ 0,//double time; // time this state was built
+ {0,0,0},//vec3_t origin;
+ {0,0,0},//vec3_t angles;
+ 0,//int number; // entity number this state is for
+ 0,//unsigned short active; // true if a valid state
+ 0,//unsigned short modelindex;
+ 0,//unsigned short frame;
+ 0,//unsigned short effects;
+ 0,//unsigned short tagentity;
+ 0,//unsigned short specialvisibilityradius;
+ 0,//unsigned short viewmodelforclient;
+ 0,//unsigned short exteriormodelforclient;
+ 0,//unsigned short nodrawtoclient;
+ 0,//unsigned short drawonlytoclient;
+ 0,//qbyte colormap;
+ 0,//qbyte skin;
+ 255,//qbyte alpha;
+ 16,//qbyte scale;
+ 0,//qbyte glowsize;
+ 254,//qbyte glowcolor;
+ 0,//qbyte flags;
+ 0,//qbyte tagindex;
+};
+
void ClearStateToDefault(entity_state_t *s)
{
+ *s = defaultstate;
+ /*
memset(s, 0, sizeof(*s));
s->alpha = 255;
s->scale = 16;
s->glowcolor = 254;
+ */
}
void EntityState_Write(entity_state_t *ent, sizebuf_t *msg, entity_state_t *delta)
vec3_t org, deltaorg;
if (ent->active)
{
+ // if not active last frame, delta from defaults
+ if (!delta->active)
+ delta = &defaultstate;
bits = 0;
VectorCopy(ent->origin, org);
VectorCopy(delta->origin, deltaorg);
}
}
}
- else if (!delta->active)
+ else if (delta->active)
MSG_WriteShort(msg, ent->number | 0x8000);
}
-void EntityState_Read(entity_state_t *e, entity_state_t *delta, int number)
+void EntityState_ReadUpdate(entity_state_t *e, int number)
{
int bits;
- memcpy(e, delta, sizeof(*e));
- e->active = true;
+ if (!e->active)
+ {
+ *e = defaultstate;
+ e->active = true;
+ }
e->time = cl.mtime[0];
e->number = number;
{
int i, onum, number;
entity_frame_t *o = &deltaframe;
- entity_state_t *ent, *delta, baseline;
+ entity_state_t *ent, *delta;
EntityFrame_AddFrame(d, f);
- ClearStateToDefault(&baseline);
EntityFrame_FetchFrame(d, d->ackframe > 0 ? d->ackframe : -1, o);
MSG_WriteByte (msg, svc_entities);
MSG_WriteLong (msg, o->framenum);
}
else
{
- // delta from baseline
- delta = &baseline;
+ // delta from defaults
+ delta = &defaultstate;
}
EntityState_Write(ent, msg, delta);
}
{
int number, removed;
entity_frame_t *f = &framedata, *delta = &deltaframe;
- entity_state_t *e, baseline, *old, *oldend, *edelta;
-
- ClearStateToDefault(&baseline);
+ entity_state_t *e, *old, *oldend;
EntityFrame_Clear(f, NULL, -1);
if (old < oldend && old->number == number)
{
// delta from old entity
- edelta = old++;
+ *e = *old++;
}
else
{
- // delta from baseline
- edelta = &baseline;
+ // delta from defaults
+ *e = defaultstate;
}
- EntityState_Read(e, edelta, number);
+ EntityState_ReadUpdate(e, number);
}
}
while (old < oldend)
{
if (f->numentities >= MAX_ENTITY_DATABASE)
Host_Error("EntityFrame_Read: entity list too big\n");
- memcpy(f->entitydata + f->numentities, old, sizeof(entity_state_t));
- f->entitydata[f->numentities].time = cl.mtime[0];
- old++;
- f->numentities++;
+ f->entitydata[f->numentities] = *old++;
+ f->entitydata[f->numentities++].time = cl.mtime[0];
}
EntityFrame_AddFrame(d, f);
}
-static int EntityFrame4_SV_ChooseCommitToReplace(entity_database4_t *d)
+int EntityFrame4_SV_ChooseCommitToReplace(entity_database4_t *d)
{
int i, best, bestframenum;
best = 0;
return best;
}
-static entity_state_t *EntityFrame4_GetReferenceEntity(entity_database4_t *d, int number)
+entity_state_t *EntityFrame4_GetReferenceEntity(entity_database4_t *d, int number)
{
if (d->maxreferenceentities <= number)
{
}
// clear the newly created entities
for (;oldmax < d->maxreferenceentities;oldmax++)
- ClearStateToDefault(d->referenceentity + oldmax);
+ d->referenceentity[oldmax] = defaultstate;
}
return d->referenceentity + number;
}
-static void EntityFrame4_AddCommitEntity(entity_database4_t *d, entity_state_t *s)
+void EntityFrame4_AddCommitEntity(entity_database4_t *d, entity_state_t *s)
{
// resize commit's entity list if full
if (d->currentcommit->maxentities <= d->currentcommit->numentities)
entity_database4_t *d;
d = Mem_Alloc(pool, sizeof(*d));
d->mempool = pool;
- d->referenceframenum = -1;
+ EntityFrame4_ResetDatabase(d);
return d;
}
{
int i;
d->referenceframenum = -1;
+ d->ackframenum = -1;
for (i = 0;i < MAX_ENTITY_HISTORY;i++)
d->commit[i].numentities = 0;
}
void EntityFrame4_AckFrame(entity_database4_t *d, int framenum)
{
int i, foundit = false;
+ entity_state_t *s;
+ entity_database4_commit_t *commit;
// check if client is requesting no delta compression
if (framenum == -1)
{
if (d->commit[i].framenum == framenum)
{
// apply commit to database
- d->referenceframenum = d->commit[i].framenum;
- while (d->commit[i].numentities--)
- *EntityFrame4_GetReferenceEntity(d, d->commit[i].entity[d->commit[i].numentities].number) = d->commit[i].entity[d->commit[i].numentities];
+ commit = d->commit + i;
+ d->referenceframenum = commit->framenum;
+ while (commit->numentities--)
+ {
+ s = commit->entity + commit->numentities;
+ *EntityFrame4_GetReferenceEntity(d, s->number) = *s;
+ }
foundit = true;
}
d->commit[i].numentities = 0;
buf.maxsize = sizeof(data);
// make the message
e = EntityFrame4_GetReferenceEntity(d, s->number);
- if (s->active)
- {
- // entity exists, send an update
- EntityState_Write(s, &buf, e);
- }
- else if (e->active)
- {
- // entity used to exist but doesn't anymore, send remove
- MSG_WriteShort(&buf, s->number | 0x8000);
- }
+ // send an update (may update or remove the entity)
+ EntityState_Write(s, &buf, e);
// if the message is empty, skip out now
if (!buf.cursize)
return true;
extern void CL_MoveLerpEntityStates(entity_t *ent);
void EntityFrame4_CL_ReadFrame(entity_database4_t *d)
{
- int i, n, number, referenceframenum, framenum;
+ int i, n, cnumber, referenceframenum, framenum, enumber, done, stopnumber;
+ entity_state_t *e;
+ // read the number of the frame this refers to
referenceframenum = MSG_ReadLong();
+ // read the number of this frame
framenum = MSG_ReadLong();
+ // read the start number
+ enumber = MSG_ReadShort();
EntityFrame4_AckFrame(d, referenceframenum);
for (i = 0;i < MAX_ENTITY_HISTORY;i++)
if (!d->commit[i].numentities)
if (i < MAX_ENTITY_HISTORY)
{
d->currentcommit = d->commit + i;
- d->currentcommit->framenum = framenum;
+ d->ackframenum = d->currentcommit->framenum = framenum;
d->currentcommit->numentities = 0;
}
else
d->currentcommit = NULL;
EntityFrame4_ResetDatabase(d);
}
- while((n = MSG_ReadShort()) != 0x8000)
+ done = false;
+ while (!done && !msg_badread)
{
- number = n & 0x7FFF;
- cl_entities[number].state_previous = cl_entities[number].state_current;
- if (number & 0x8000)
+ n = (unsigned short)MSG_ReadShort();
+ if (n == 0x8000)
{
- ClearStateToDefault(&cl_entities[number].state_current);
- cl_entities[number].state_current.active = false;
- cl_entities[number].state_current.number = number;
+ // no more entities in this update, but we still need to copy the
+ // rest of the reference entities
+ done = true;
+ // read end of range number, then process normally
+ n = (unsigned short)MSG_ReadShort();
+ }
+ // high bit means it's a remove message
+ cnumber = n & 0x7FFF;
+ // add one (the changed one) if not done
+ stopnumber = cnumber + !done;
+ // process entities in range from the last one to the changed one
+ for (;enumber < stopnumber;enumber++)
+ {
+ e = EntityFrame4_GetReferenceEntity(d, enumber);
+ cl_entities[enumber].state_previous = cl_entities[enumber].state_current;
+ // skipped (unchanged), copy from reference database
+ cl_entities[enumber].state_current = *e;
+ if (enumber == cnumber)
+ {
+ // modified
+ if (n & 0x8000)
+ {
+ // simply removed
+ cl_entities[enumber].state_current = defaultstate;
+ }
+ else
+ {
+ // read the changes
+ EntityState_ReadUpdate(&cl_entities[enumber].state_current, enumber);
+ }
+ }
+ cl_entities[enumber].state_current.number = enumber;
+ if (cl_entities[enumber].state_current.active != cl_entities[enumber].state_previous.active)
+ {
+ if (cl_entities[enumber].state_current.active)
+ Con_Printf("entity #%i has become active\n");
+ else if (cl_entities[enumber].state_current.active)
+ Con_Printf("entity #%i has become inactive\n");
+ }
+ CL_MoveLerpEntityStates(&cl_entities[enumber]);
+ cl_entities_active[enumber] = true;
+ if (d->currentcommit)
+ EntityFrame4_AddCommitEntity(d, &cl_entities[enumber].state_current);
}
- else
- EntityState_Read(&cl_entities[number].state_current, EntityFrame4_GetReferenceEntity(d, number), number);
- CL_MoveLerpEntityStates(&cl_entities[number]);
- cl_entities_active[number] = true;
- if (d->currentcommit)
- EntityFrame4_AddCommitEntity(d, &cl_entities[number].state_current);
}
d->currentcommit = NULL;
}
+
#define E_UNUSED7 (1<<30)
#define E_EXTEND4 (1<<31)
+// clears a state to baseline values
void ClearStateToDefault(entity_state_t *s);
+// used by some of the DP protocols
+void EntityState_Write(entity_state_t *ent, sizebuf_t *msg, entity_state_t *delta);
// (server) clears the database to contain no frames (thus delta compression
// compresses against nothing)
// commits waiting to be applied to the reference database when confirmed
// (commit[i]->numentities == 0 means it is empty)
entity_database4_commit_t commit[MAX_ENTITY_HISTORY];
- // used only while building a commit
+ // (server only) the current commit being worked on
entity_database4_commit_t *currentcommit;
+ // (server only) if a commit won't fit entirely, continue where it left
+ // off next frame
+ int currententitynumber;
+ // (client only) most recently received frame number to be sent in next
+ // input update
+ int ackframenum;
}
entity_database4_t;
+// should-be-private functions that aren't
+int EntityFrame4_SV_ChooseCommitToReplace(entity_database4_t *d);
+entity_state_t *EntityFrame4_GetReferenceEntity(entity_database4_t *d, int number);
+void EntityFrame4_AddCommitEntity(entity_database4_t *d, entity_state_t *s);
+
// allocate a database
entity_database4_t *EntityFrame4_AllocDatabase(mempool_t *pool);
// free a database
// PGM 01/19/97 - team color drawing
// PGM 03/02/97 - fixed so color swatch only appears in CTF modes
- if (gamemode == GAME_ROGUE && (cl.maxclients != 1) && (teamplay.integer > 3) && (teamplay.integer < 7))
+ if (gamemode == GAME_ROGUE && !cl.islocalgame && (teamplay.integer > 3) && (teamplay.integer < 7))
{
char num[12];
scoreboard_t *s;
{
if (gamemode != GAME_GOODVSBAD2)
Sbar_DrawInventory ();
- if (cl.maxclients != 1)
+ if (!cl.islocalgame)
Sbar_DrawFrags ();
}
typedef struct
{
- int maxclients;
- // [maxclients]
- struct client_s *clients;
+ // NULL pointers are non-existent clients
+ struct client_s *connectedclients[MAX_SCOREBOARD];
// episode completion information
int serverflags;
// cleared when at SV_SpawnServer
typedef struct client_s
{
- // false = client is free
- qboolean active;
// false = don't send datagrams
qboolean spawned;
// has been told to go to another level
qboolean sendsignon;
// remove this client immediately
qboolean deadsocket;
+ // index of this client in the svs.connectedclients pointer array
+ int number;
// reliable messages must be sent periodically
double last_message;
#ifdef QUAKEENTITIES
// delta compression state
float nextfullupdate[MAX_EDICTS];
-#else
+#elif 0
entity_database_t entitydatabase;
int entityframenumber; // incremented each time an entity frame is sent
+#else
+ entity_database4_t *entitydatabase4;
+ int entityframenumber; // incremented each time an entity frame is sent
#endif
} client_t;
extern cvar_t sv_stepheight;
extern cvar_t sv_jumpstep;
+extern mempool_t *sv_clients_mempool;
+extern mempool_t *sv_edicts_mempool;
+
// persistant server info
extern server_static_t svs;
// local server
void SV_SaveSpawnparms (void);
void SV_SpawnServer (const char *server);
-void SV_SetMaxClients(int n);
-
void SV_CheckVelocity (edict_t *ent);
#endif
static char localmodels[MAX_MODELS][5]; // inline model names for precache
mempool_t *sv_edicts_mempool = NULL;
+mempool_t *sv_clients_mempool = NULL;
//============================================================================
for (i = 0;i < MAX_MODELS;i++)
sprintf (localmodels[i], "*%i", i);
- sv_edicts_mempool = Mem_AllocPool("edicts");
+ sv_edicts_mempool = Mem_AllocPool("server edicts");
+ sv_clients_mempool = Mem_AllocPool("server clients");
}
/*
char message[128];
// edicts get reallocated on level changes, so we need to update it here
- client->edict = EDICT_NUM((client - svs.clients) + 1);
+ client->edict = EDICT_NUM(client->number + 1);
// LordHavoc: clear entityframe tracking
client->entityframenumber = 0;
- EntityFrame_ClearDatabase(&client->entitydatabase);
+ if (client->entitydatabase4)
+ EntityFrame4_FreeDatabase(client->entitydatabase4);
+ client->entitydatabase4 = EntityFrame4_AllocDatabase(sv_clients_mempool);
MSG_WriteByte (&client->message, svc_print);
sprintf (message, "\002\nServer: %s build %s (progs %i crc)", gamename, buildstring, pr_crc);
MSG_WriteString (&client->message,message);
MSG_WriteByte (&client->message, svc_serverinfo);
- MSG_WriteLong (&client->message, DPPROTOCOL_VERSION3);
- MSG_WriteByte (&client->message, svs.maxclients);
+ MSG_WriteLong (&client->message, DPPROTOCOL_VERSION4);
+ MSG_WriteByte (&client->message, MAX_SCOREBOARD);
if (!coop.integer && deathmatch.integer)
MSG_WriteByte (&client->message, GAME_DEATHMATCH);
int i;
float spawn_parms[NUM_SPAWN_PARMS];
- client = svs.clients + clientnum;
+ client = svs.connectedclients[clientnum];
// set up the client_t
if (sv.loadgame)
strcpy(client->name, "unconnected");
strcpy(client->old_name, "unconnected");
- client->active = true;
+ client->number = clientnum;
client->spawned = false;
client->edict = EDICT_NUM(clientnum+1);
client->message.data = client->msgbuf;
// we can omit invisible entities with no effects that are not clients
// LordHavoc: this could kill tags attached to an invisible entity, I
// just hope we never have to support that case
- if (cs.number > svs.maxclients && ((cs.effects & EF_NODRAW) || (!cs.modelindex && !cs.specialvisibilityradius)))
+ if (cs.number > MAX_SCOREBOARD && ((cs.effects & EF_NODRAW) || (!cs.modelindex && !cs.specialvisibilityradius)))
continue;
sendentitiesindex[e] = sendentities + numsendentities;
sendentities[numsendentities++] = cs;
static int sv_writeentitiestoclient_culled_trace;
static int sv_writeentitiestoclient_visibleentities;
static int sv_writeentitiestoclient_totalentities;
-static entity_frame_t sv_writeentitiestoclient_entityframe;
+//static entity_frame_t sv_writeentitiestoclient_entityframe;
static int sv_writeentitiestoclient_clentnum;
static qbyte *sv_writeentitiestoclient_pvs;
static vec3_t sv_writeentitiestoclient_testeye;
int i;
vec3_t testorigin;
entity_state_t *s;
+ entity_database4_t *d;
+ int maxbytes, n, startnumber;
+ entity_state_t *e, inactiveentitystate;
+ sizebuf_t buf;
+ qbyte data[128];
+ // prepare the buffer
+ memset(&buf, 0, sizeof(buf));
+ buf.data = data;
+ buf.maxsize = sizeof(data);
+
+ // this state's number gets played around with later
+ ClearStateToDefault(&inactiveentitystate);
+ //inactiveentitystate = defaultstate;
sv_writeentitiestoclient_client = client;
for (i = 0;i < numsendentities;i++)
SV_MarkWriteEntityStateToClient(sendentities + i);
- EntityFrame_Clear(&sv_writeentitiestoclient_entityframe, testorigin, ++client->entityframenumber);
- for (i = 0;i < numsendentities;i++)
+ d = client->entitydatabase4;
+ // calculate maximum bytes to allow in this packet
+ // deduct 4 to account for the end data
+ maxbytes = min(msg->maxsize, MAX_PACKETFRAGMENT) - 4;
+
+ d->currentcommit = d->commit + EntityFrame4_SV_ChooseCommitToReplace(d);
+ d->currentcommit->numentities = 0;
+ d->currentcommit->framenum = ++client->entityframenumber;
+ MSG_WriteByte(msg, svc_entities);
+ MSG_WriteLong(msg, d->referenceframenum);
+ MSG_WriteLong(msg, d->currentcommit->framenum);
+ if (d->currententitynumber >= sv.max_edicts)
+ startnumber = 1;
+ else
+ startnumber = bound(1, d->currententitynumber, sv.max_edicts - 1);
+ MSG_WriteShort(msg, startnumber);
+ // reset currententitynumber so if the loop does not break it we will
+ // start at beginning next frame (if it does break, it will set it)
+ d->currententitynumber = 1;
+ for (i = 0, n = startnumber;n < sv.max_edicts;n++)
{
- s = sendentities + i;
- if (sententities[s->number] == sententitiesmark)
+ // find the old state to delta from
+ e = EntityFrame4_GetReferenceEntity(d, n);
+ // prepare the buffer
+ SZ_Clear(&buf);
+ // make the message
+ if (sententities[n] == sententitiesmark)
{
+ // entity exists, build an update (if empty there is no change)
+ // find the state in the list
+ for (;i < numsendentities && sendentities[i].number < n;i++);
+ s = sendentities + i;
+ if (s->number != n)
+ Sys_Error("SV_WriteEntitiesToClient: s->number != n\n");
+ // build the update
if (s->exteriormodelforclient && s->exteriormodelforclient == sv_writeentitiestoclient_clentnum)
{
s->flags |= RENDER_EXTERIORMODEL;
- EntityFrame_AddEntity(&sv_writeentitiestoclient_entityframe, s);
+ EntityState_Write(s, &buf, e);
s->flags &= ~RENDER_EXTERIORMODEL;
}
else
- EntityFrame_AddEntity(&sv_writeentitiestoclient_entityframe, s);
+ EntityState_Write(s, &buf, e);
+ }
+ else
+ {
+ s = &inactiveentitystate;
+ s->number = n;
+ if (e->active)
+ {
+ // entity used to exist but doesn't anymore, send remove
+ MSG_WriteShort(&buf, n | 0x8000);
+ }
+ }
+ // if the commit is full, we're done this frame
+ if (msg->cursize + buf.cursize > maxbytes)
+ {
+ // next frame we will continue where we left off
+ break;
+ }
+ // add the entity to the commit
+ EntityFrame4_AddCommitEntity(d, s);
+ // if the message is empty, skip out now
+ if (buf.cursize)
+ {
+ // write the message to the packet
+ SZ_Write(msg, buf.data, buf.cursize);
}
}
- EntityFrame_Write(&client->entitydatabase, &sv_writeentitiestoclient_entityframe, msg);
+ d->currententitynumber = n;
+
+ // remove world message (invalid, and thus a good terminator)
+ MSG_WriteShort(msg, 0x8000);
+ // write the number of the end entity
+ MSG_WriteShort(msg, d->currententitynumber);
+ // just to be sure
+ d->currentcommit = NULL;
if (sv_cullentities_stats.integer)
Con_Printf("client \"%s\" entities: %d total, %d visible, %d culled by: %d pvs %d portal %d trace\n", client->name, sv_writeentitiestoclient_totalentities, sv_writeentitiestoclient_visibleentities, sv_writeentitiestoclient_culled_pvs + sv_writeentitiestoclient_culled_portal + sv_writeentitiestoclient_culled_trace, sv_writeentitiestoclient_culled_pvs, sv_writeentitiestoclient_culled_portal, sv_writeentitiestoclient_culled_trace);
char *s;
// check for changes to be sent over the reliable streams
- for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
{
// only update the client fields if they've spawned in
- if (host_client->spawned)
+ if ((host_client = svs.connectedclients[i]) && host_client->spawned)
{
// update the host_client fields we care about according to the entity fields
sv_player = host_client->edict;
if (strcmp(host_client->old_name, host_client->name))
{
strcpy(host_client->old_name, host_client->name);
- for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
+ for (j = 0;j < MAX_SCOREBOARD;j++)
{
- if (!client->active || !client->spawned)
+ if (!(client = svs.connectedclients[j]) || !client->spawned)
continue;
MSG_WriteByte (&client->message, svc_updatename);
MSG_WriteByte (&client->message, i);
if (host_client->old_colors != host_client->colors)
{
host_client->old_colors = host_client->colors;
- for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
+ for (j = 0;j < MAX_SCOREBOARD;j++)
{
- if (!client->active || !client->spawned)
+ if (!(client = svs.connectedclients[j]) || !client->spawned)
continue;
MSG_WriteByte (&client->message, svc_updatecolors);
MSG_WriteByte (&client->message, i);
if (host_client->old_frags != host_client->frags)
{
host_client->old_frags = host_client->frags;
- for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
+ for (j = 0;j < MAX_SCOREBOARD;j++)
{
- if (!client->active || !client->spawned)
+ if (!(client = svs.connectedclients[j]) || !client->spawned)
continue;
MSG_WriteByte (&client->message, svc_updatefrags);
MSG_WriteByte (&client->message, i);
}
}
- for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
- {
- if (!client->active)
- continue;
- SZ_Write (&client->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize);
- }
+ for (j = 0;j < MAX_SCOREBOARD;j++)
+ if ((client = svs.connectedclients[j]))
+ SZ_Write (&client->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize);
SZ_Clear (&sv.reliable_datagram);
}
SV_UpdateToReliableMessages();
// build individual updates
- for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
{
- if (!host_client->active)
+ if (!(host_client = svs.connectedclients[i]))
continue;
- if (host_client->deadsocket)
+ if (host_client->deadsocket || !host_client->netconnection || host_client->message.overflowed)
{
SV_DropClient (true); // if the message couldn't send, kick off
continue;
}
}
- // check for an overflowed message. Should only happen
- // on a very fucked up connection that backs up a lot, then
- // changes level
- if (host_client->message.overflowed)
- {
- SV_DropClient (true); // overflowed
- host_client->message.overflowed = false;
- continue;
- }
-
if (host_client->message.cursize || host_client->dropasap)
{
if (!NetConn_CanSendMessage (host_client->netconnection))
if (svent->e->free)
continue;
- if (entnum > svs.maxclients && !svent->v->modelindex)
+ if (entnum > MAX_SCOREBOARD && !svent->v->modelindex)
continue;
// create entity baseline
VectorCopy (svent->v->angles, svent->e->baseline.angles);
svent->e->baseline.frame = svent->v->frame;
svent->e->baseline.skin = svent->v->skin;
- if (entnum > 0 && entnum <= svs.maxclients)
+ if (entnum > 0 && entnum <= MAX_SCOREBOARD)
{
svent->e->baseline.colormap = entnum;
svent->e->baseline.modelindex = SV_ModelIndex("progs/player.mdl");
svs.serverflags = pr_global_struct->serverflags;
- for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
{
- if (!host_client->active)
+ if (!(host_client = svs.connectedclients[i]))
continue;
// call the progs to get default spawn parms for the new client
}
SV_ClearWorld();
- sv.max_edicts = min(sv.max_edicts + 32, MAX_EDICTS);
+ sv.max_edicts = min(sv.max_edicts + 256, MAX_EDICTS);
sv.edictsengineprivate = Mem_Alloc(sv_edicts_mempool, sv.max_edicts * sizeof(edict_engineprivate_t));
sv.edictsfields = Mem_Alloc(sv_edicts_mempool, sv.max_edicts * pr_edict_size);
sv.moved_edicts = Mem_Alloc(sv_edicts_mempool, sv.max_edicts * sizeof(edict_t *));
// tell all connected clients that we are going to a new level
//
if (sv.active)
- SV_SendReconnect ();
+ SV_SendReconnect();
else
- NetConn_OpenServerPorts(svs.maxclients > 1);
+ NetConn_OpenServerPorts(true);
//
// make cvars consistant
// allocate server memory
// start out with just enough room for clients and a reasonable estimate of entities
- sv.max_edicts = ((svs.maxclients + 128) + 31) & ~31;
+ sv.max_edicts = max(MAX_SCOREBOARD + 1, 512);
sv.max_edicts = min(sv.max_edicts, MAX_EDICTS);
// clear the edict memory pool
sv.signon.data = sv.signon_buf;
// leave slots at start for clients only
- sv.num_edicts = svs.maxclients+1;
+ sv.num_edicts = MAX_SCOREBOARD+1;
sv.state = ss_loading;
sv.paused = false;
#endif
// send serverinfo to all connected clients
- for (i=0,host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
- if (host_client->active)
- SV_SendServerinfo (host_client);
+ for (i = 0;i < MAX_SCOREBOARD;i++)
+ if ((host_client = svs.connectedclients[i]))
+ SV_SendServerinfo(host_client);
Con_DPrintf ("Server spawned.\n");
NetConn_Heartbeat (2);
SV_LinkEdict (ent, true);
}
-/*
-=============
-SV_Physics_Noclip
-
-A moving object that doesn't obey physics
-=============
-*/
-void SV_Physics_Noclip (edict_t *ent)
-{
- // regular thinking
- if (!SV_RunThink (ent))
- return;
-
- VectorMA (ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
- VectorMA (ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin);
-
- SV_LinkEdict (ent, false);
-}
-
/*
==============================================================================
if (pr_global_struct->force_retouch)
SV_LinkEdict (ent, true); // force retouch even for stationary
- if (i > 0 && i <= svs.maxclients)
+ if (i > 0 && i <= MAX_SCOREBOARD)
{
- if (!svs.clients[i-1].active)
+ if (!svs.connectedclients[i-1])
continue;
// connected slot
// call standard client pre-think
SV_Physics_Follow (ent);
break;
case MOVETYPE_NOCLIP:
- if (i > 0 && i <= svs.maxclients)
+ if (SV_RunThink(ent))
{
- if (SV_RunThink (ent))
- {
- SV_CheckWater (ent);
- VectorMA (ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin);
- VectorMA (ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
- }
+ SV_CheckWater(ent);
+ VectorMA(ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin);
+ VectorMA(ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
}
- else
- SV_Physics_Noclip (ent);
+ // relink normal entities here, players always get relinked so don't relink twice
+ if (!(i > 0 && i <= MAX_SCOREBOARD))
+ SV_LinkEdict(ent, false);
break;
case MOVETYPE_STEP:
SV_Physics_Step (ent);
SV_Physics_Toss (ent);
break;
case MOVETYPE_FLY:
- if (i > 0 && i <= svs.maxclients)
+ if (i > 0 && i <= MAX_SCOREBOARD)
{
if (SV_RunThink (ent))
{
break;
}
- if (i > 0 && i <= svs.maxclients && !ent->e->free)
+ if (i > 0 && i <= MAX_SCOREBOARD && !ent->e->free)
{
SV_CheckVelocity (ent);
val->_float = host_client->ping * 1000.0;
// read current angles
- // dpprotocol version 2
+ // DPPROTOCOL_VERSION4
for (i = 0;i < 3;i++)
- angle[i] = MSG_ReadFloat ();
+ angle[i] = MSG_ReadPreciseAngle();
VectorCopy (angle, sv_player->v->v_angle);
extern void SV_SendServerinfo(client_t *client);
void SV_ReadClientMessage(void)
{
- int cmd;
+ int cmd, clientnum = host_client->number;
char *s;
//MSG_BeginReading ();
for(;;)
{
- if (!host_client->active)
+ if (!(host_client = svs.connectedclients[clientnum]))
{
// a command caused an error
SV_DropClient (false);
break;
case clc_ackentities:
- EntityFrame_AckFrame(&host_client->entitydatabase, MSG_ReadLong());
+ //if (dpprotocol == DPPROTOCOL_VERSION1 || dpprotocol == DPPROTOCOL_VERSION2 || dpprotocol == DPPROTOCOL_VERSION3)
+ // EntityFrame_AckFrame(&host_client->entitydatabase, MSG_ReadLong());
+ //else
+ EntityFrame4_AckFrame(host_client->entitydatabase4, MSG_ReadLong());
break;
}
}
{
int i;
- for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
{
- if (!host_client->active)
+ if (!(host_client = svs.connectedclients[i]))
continue;
sv_player = host_client->edict;