upgraded network protocol to DPPROTOCOL_VERSION4 - this means partial entity updates...
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 6 Aug 2003 13:16:48 +0000 (13:16 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 6 Aug 2003 13:16:48 +0000 (13:16 +0000)
svs.clients gone, replaced with svs.connectedclients (similar idea except this is an array of pointers, and they are NULL for any unconnected client slots), this means entirely dynamic memory usage depending on number of clients (at least in the server; the client still needs fixing), this also means "maxplayers" is now a cvar (sv_maxplayers internally), not a command
some fixes/cleanups/tweaks (like proper setup of default maxplayers and deathmatch cvar in multiplayer-only games incase someone starts a map from console right away without using the menus)

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@3367 d7cf8633-e32d-0410-b094-e92efae38249

20 files changed:
cl_input.c
cl_main.c
cl_parse.c
client.h
common.c
gl_rmain.c
host.c
host_cmd.c
menu.c
netconn.c
netconn.h
pr_cmds.c
pr_edict.c
protocol.c
protocol.h
sbar.c
server.h
sv_main.c
sv_phys.c
sv_user.c

index f92087c..6463fed 100644 (file)
@@ -246,6 +246,7 @@ cvar_t cl_pitchspeed = {CVAR_SAVE, "cl_pitchspeed","150"};
 
 cvar_t cl_anglespeedkey = {CVAR_SAVE, "cl_anglespeedkey","1.5"};
 
+cvar_t cl_nodelta = {0, "cl_nodelta", "0"};
 
 /*
 ================
@@ -391,7 +392,7 @@ void CL_SendMove(usercmd_t *cmd)
                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]);
@@ -430,12 +431,26 @@ void CL_SendMove(usercmd_t *cmd)
        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
@@ -509,5 +524,7 @@ void CL_InitInput (void)
        Cmd_AddCommand ("-button7", IN_Button7Up);
        Cmd_AddCommand ("+button8", IN_Button8Down);
        Cmd_AddCommand ("-button8", IN_Button8Up);
+
+       Cvar_RegisterVariable(&cl_nodelta);
 }
 
index 18503b6..368a78f 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -98,6 +98,7 @@ void CL_ClearState(void)
        if (!sv.active)
                Host_ClearMemory ();
 
+       // note: this also gets rid of the entity database
        Mem_EmptyPool(cl_entities_mempool);
 
 // wipe the entire cl structure
@@ -349,7 +350,7 @@ static float CL_LerpPoint(void)
 
        // 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;
index f0beef0..c8ac50b 100644 (file)
@@ -331,9 +331,9 @@ void CL_ParseServerInfo (void)
 
 // 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;
@@ -342,7 +342,7 @@ void CL_ParseServerInfo (void)
        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
@@ -532,7 +532,7 @@ void CL_MoveLerpEntityStates(entity_t *ent)
                // 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];
@@ -653,9 +653,10 @@ void CL_ParseUpdate (int bits)
 }
 
 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;
@@ -674,7 +675,11 @@ void CL_ReadEntityFrame(void)
                }
        }
        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)
@@ -683,7 +688,7 @@ 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
@@ -711,7 +716,7 @@ void CL_ParseBaseline (entity_t *ent, int large)
 {
        int i;
 
-       memset(&ent->state_baseline, 0, sizeof(entity_state_t));
+       ClearStateToDefault(&ent->state_baseline);
        ent->state_baseline.active = true;
        if (large)
        {
@@ -730,13 +735,8 @@ void CL_ParseBaseline (entity_t *ent, int 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;
 }
 
 
@@ -1504,15 +1504,15 @@ void CL_ParseServerMessage(void)
 
                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;
 
index 4713f19..c0350f8 100644 (file)
--- a/client.h
+++ b/client.h
@@ -308,6 +308,9 @@ extern client_static_t      cls;
 //
 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;
@@ -424,7 +427,7 @@ typedef struct
 
        // entity database stuff
        entity_database_t entitydatabase;
-       entity_database4_t entitydatabase4;
+       entity_database4_t *entitydatabase4;
 }
 client_state_t;
 
index c446123..b337893 100644 (file)
--- a/common.c
+++ b/common.c
@@ -362,7 +362,7 @@ float MSG_ReadDPCoord (void)
 // 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();
index 91f2316..8c24694 100644 (file)
@@ -1125,7 +1125,7 @@ R_SetupFrame
 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");
diff --git a/host.c b/host.c
index 3bd6992..2ca9ade 100644 (file)
--- a/host.c
+++ b/host.c
@@ -184,6 +184,9 @@ void Host_ServerOptions (void)
 {
        int i, numplayers;
 
+       // general default
+       numplayers = 8;
+
        if (cl_available)
        {
                // client exists, check what mode the user wants
@@ -191,7 +194,7 @@ void Host_ServerOptions (void)
                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"))
@@ -199,65 +202,51 @@ void Host_ServerOptions (void)
                }
                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
@@ -364,17 +353,20 @@ void SV_BroadcastPrintf(const char *fmt, ...)
        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);
@@ -420,8 +412,6 @@ void SV_DropClient(qboolean crash)
        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
@@ -446,27 +436,30 @@ void SV_DropClient(qboolean crash)
                }
        }
 
-       // 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);
 }
 
 /*
@@ -504,9 +497,10 @@ void Host_ShutdownServer(qboolean crash)
                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))
                                {
@@ -531,8 +525,8 @@ void Host_ShutdownServer(qboolean crash)
        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();
@@ -541,7 +535,6 @@ void Host_ShutdownServer(qboolean crash)
 // clear structures
 //
        memset(&sv, 0, sizeof(sv));
-       memset(svs.clients, 0, svs.maxclients * sizeof(client_t));
 }
 
 
@@ -665,14 +658,14 @@ void Host_ServerFrame (void)
 {
        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;
@@ -726,6 +719,8 @@ void _Host_Frame (float time)
                return;
        }
 
+       cl.islocalgame = NetConn_IsLocalGame();
+
        // get new key events
        Sys_SendKeyEvents();
 
@@ -841,11 +836,9 @@ void Host_Frame (float time)
        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);
 }
index 000e43a..7c02d6c 100644 (file)
@@ -66,16 +66,16 @@ void Host_Status_f (void)
        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;
@@ -214,9 +214,9 @@ void Host_Ping_f (void)
        }
 
        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++)
@@ -390,10 +390,21 @@ void Host_Savegame_f (void)
                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)
@@ -408,15 +419,6 @@ void Host_Savegame_f (void)
                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");
@@ -433,7 +435,7 @@ void Host_Savegame_f (void)
        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);
@@ -613,7 +615,7 @@ void Host_PerformLoadGame(char *name)
        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))
@@ -665,7 +667,7 @@ void Host_Name_f (void)
 // 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);
 }
 
@@ -732,8 +734,8 @@ void Host_Say(qboolean teamonly)
        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;
 
@@ -809,14 +811,9 @@ void Host_Tell_f(void)
        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;
 }
 
@@ -890,7 +887,7 @@ void Host_Color_f(void)
 
                // 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);
        }
 }
@@ -1084,8 +1081,10 @@ void Host_Spawn_f (void)
        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);
@@ -1189,25 +1188,22 @@ void Host_Kick_f (void)
        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)
                {
@@ -1544,7 +1540,7 @@ void Host_Startdemos_f (void)
 {
        int             i, c;
 
-       if (cls.state == ca_dedicated)
+       if (cls.state == ca_dedicated || sv_maxplayers.integer > 1)
        {
                if (!sv.active && !sv_spawnmap[0])
                {
diff --git a/menu.c b/menu.c
index b00ba28..d7fe96a 100644 (file)
--- a/menu.c
+++ b/menu.c
@@ -709,7 +709,7 @@ void M_Menu_Save_f (void)
                return;
        if (cl.intermission)
                return;
-       if (svs.maxclients != 1)
+       if (!cl.islocalgame)
                return;
        m_entersound = true;
        m_state = m_save;
@@ -2941,7 +2941,7 @@ void M_Menu_GameOptions_f (void)
        m_state = m_gameoptions;
        m_entersound = true;
        if (maxplayers == 0)
-               maxplayers = svs.maxclients;
+               maxplayers = sv_maxplayers.integer;
        if (maxplayers < 2)
                maxplayers = MAX_SCOREBOARD;
 }
index c7dc533..559203e 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -25,6 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #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"};
 
@@ -549,6 +550,20 @@ void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peer
        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;
@@ -973,10 +988,10 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length,
                                                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)
@@ -997,30 +1012,48 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length,
                                                        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);
-                                                                       }
-                                                               }
                                                        }
                                                }
                                        }
@@ -1033,13 +1066,13 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length,
                                // 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))
@@ -1099,10 +1132,10 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length,
                                                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)
@@ -1131,10 +1164,11 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length,
                                                        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
@@ -1189,7 +1223,7 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length,
                                                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);
@@ -1264,14 +1298,25 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length,
                        }
                }
 #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);
+                               }
                        }
                }
        }
@@ -1287,9 +1332,9 @@ void NetConn_ServerFrame(void)
        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;
@@ -1364,7 +1409,7 @@ void NetConn_Heartbeat(int priority)
 
        // 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++)
@@ -1391,9 +1436,9 @@ int NetConn_SendToAll(sizebuf_t *data, double blocktime)
                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))
                                {
@@ -1410,30 +1455,6 @@ int NetConn_SendToAll(sizebuf_t *data, double blocktime)
        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);
@@ -1478,7 +1499,6 @@ void NetConn_Init(void)
        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);
@@ -1491,6 +1511,7 @@ void NetConn_Init(void)
        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++)
index cfde20d..2e9d923 100755 (executable)
--- a/netconn.h
+++ b/netconn.h
@@ -191,6 +191,7 @@ extern unsigned short ntohs (unsigned short netshort);
 //
 //============================================================================
 
+extern cvar_t sv_maxplayers;
 extern sizebuf_t net_message;
 
 int NetConn_SendReliableMessage(netconn_t *conn, sizebuf_t *data);
@@ -209,6 +210,7 @@ void NetConn_Shutdown(void);
 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);
index 72c7233..956e01e 100644 (file)
--- a/pr_cmds.c
+++ b/pr_cmds.c
@@ -372,16 +372,15 @@ void PF_sprint (void)
        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 );
 }
 
 
@@ -403,16 +402,15 @@ void PF_centerprint (void)
        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 );
 }
 
 
@@ -830,35 +828,25 @@ int PF_newcheckclient (int check)
 
 // 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;
        }
 
@@ -946,13 +934,13 @@ void PF_stuffcmd (void)
        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;
 }
 
@@ -1130,7 +1118,7 @@ void PF_Remove (void)
        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);
 }
@@ -1454,13 +1442,15 @@ void PF_lightstyle (void)
        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)
@@ -1741,9 +1731,9 @@ sizebuf_t *WriteDest (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;
@@ -1853,12 +1843,10 @@ void PF_setspawnparms (void)
 
        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];
 }
@@ -2094,13 +2082,12 @@ void PF_setcolor (void)
        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;
@@ -2855,15 +2842,15 @@ void PF_strcat(void)
 //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);
@@ -2900,11 +2887,11 @@ void PF_clientcommand (void)
 
        //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;
 }
@@ -2977,7 +2964,7 @@ void PF_setattachment (void)
                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;
index 9f22b1e..2e48778 100644 (file)
@@ -199,11 +199,11 @@ void ED_ClearEdict (edict_t *e)
        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);
        }
 }
 
@@ -223,7 +223,7 @@ edict_t *ED_Alloc (void)
        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
index 32624a4..c207835 100644 (file)
@@ -1,12 +1,41 @@
 
 #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)
@@ -15,6 +44,9 @@ void EntityState_Write(entity_state_t *ent, sizebuf_t *msg, entity_state_t *delt
        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);
@@ -168,15 +200,18 @@ void EntityState_Write(entity_state_t *ent, sizebuf_t *msg, entity_state_t *delt
                        }
                }
        }
-       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;
 
@@ -390,11 +425,10 @@ void EntityFrame_Write(entity_database_t *d, entity_frame_t *f, sizebuf_t *msg)
 {
        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);
@@ -422,8 +456,8 @@ void EntityFrame_Write(entity_database_t *d, entity_frame_t *f, sizebuf_t *msg)
                }
                else
                {
-                       // delta from baseline
-                       delta = &baseline;
+                       // delta from defaults
+                       delta = &defaultstate;
                }
                EntityState_Write(ent, msg, delta);
        }
@@ -441,9 +475,7 @@ void EntityFrame_Read(entity_database_t *d)
 {
        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);
 
@@ -496,25 +528,23 @@ void EntityFrame_Read(entity_database_t *d)
                        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);
 }
@@ -534,7 +564,7 @@ int EntityFrame_MostRecentlyRecievedFrameNum(entity_database_t *d)
 
 
 
-static int EntityFrame4_SV_ChooseCommitToReplace(entity_database4_t *d)
+int EntityFrame4_SV_ChooseCommitToReplace(entity_database4_t *d)
 {
        int i, best, bestframenum;
        best = 0;
@@ -552,7 +582,7 @@ static int EntityFrame4_SV_ChooseCommitToReplace(entity_database4_t *d)
        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)
        {
@@ -567,12 +597,12 @@ static entity_state_t *EntityFrame4_GetReferenceEntity(entity_database4_t *d, in
                }
                // 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)
@@ -594,7 +624,7 @@ entity_database4_t *EntityFrame4_AllocDatabase(mempool_t *pool)
        entity_database4_t *d;
        d = Mem_Alloc(pool, sizeof(*d));
        d->mempool = pool;
-       d->referenceframenum = -1;
+       EntityFrame4_ResetDatabase(d);
        return d;
 }
 
@@ -613,6 +643,7 @@ void EntityFrame4_ResetDatabase(entity_database4_t *d)
 {
        int i;
        d->referenceframenum = -1;
+       d->ackframenum = -1;
        for (i = 0;i < MAX_ENTITY_HISTORY;i++)
                d->commit[i].numentities = 0;
 }
@@ -620,6 +651,8 @@ void EntityFrame4_ResetDatabase(entity_database4_t *d)
 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)
        {
@@ -633,9 +666,13 @@ void EntityFrame4_AckFrame(entity_database4_t *d, int framenum)
                        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;
@@ -667,16 +704,8 @@ int EntityFrame4_SV_WriteFrame_Entity(entity_database4_t *d, sizebuf_t *msg, int
        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;
@@ -702,9 +731,14 @@ void EntityFrame4_SV_WriteFrame_End(entity_database4_t *d, sizebuf_t *msg)
 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)
@@ -712,7 +746,7 @@ void EntityFrame4_CL_ReadFrame(entity_database4_t *d)
        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
@@ -721,23 +755,58 @@ void EntityFrame4_CL_ReadFrame(entity_database4_t *d)
                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;
 }
 
+
index 3950fa4..9ec2011 100644 (file)
@@ -483,7 +483,10 @@ entity_frame_t;
 #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)
@@ -531,11 +534,22 @@ typedef struct entity_database4_s
        // 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
diff --git a/sbar.c b/sbar.c
index 6b978df..0f39e37 100644 (file)
--- a/sbar.c
+++ b/sbar.c
@@ -635,7 +635,7 @@ void Sbar_DrawFace (void)
 
 // 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;
@@ -780,7 +780,7 @@ void Sbar_Draw (void)
        {
                if (gamemode != GAME_GOODVSBAD2)
                        Sbar_DrawInventory ();
-               if (cl.maxclients != 1)
+               if (!cl.islocalgame)
                        Sbar_DrawFrags ();
        }
 
index aa2493f..ed1c89f 100644 (file)
--- a/server.h
+++ b/server.h
@@ -24,9 +24,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 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
@@ -100,8 +99,6 @@ typedef struct
 
 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
@@ -110,6 +107,8 @@ typedef struct client_s
        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;
@@ -149,9 +148,12 @@ typedef struct client_s
 #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;
 
@@ -246,6 +248,9 @@ extern cvar_t sv_aim;
 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
@@ -296,8 +301,6 @@ void SV_RunClients (void);
 void SV_SaveSpawnparms (void);
 void SV_SpawnServer (const char *server);
 
-void SV_SetMaxClients(int n);
-
 void SV_CheckVelocity (edict_t *ent);
 
 #endif
index 4421ae8..d96e119 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -34,6 +34,7 @@ server_static_t svs;
 static char localmodels[MAX_MODELS][5];                        // inline model names for precache
 
 mempool_t *sv_edicts_mempool = NULL;
+mempool_t *sv_clients_mempool = NULL;
 
 //============================================================================
 
@@ -72,7 +73,8 @@ void SV_Init (void)
        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");
 }
 
 /*
@@ -248,19 +250,21 @@ void SV_SendServerinfo (client_t *client)
        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);
@@ -307,7 +311,7 @@ void SV_ConnectClient (int clientnum, netconn_t *netconnection)
        int                             i;
        float                   spawn_parms[NUM_SPAWN_PARMS];
 
-       client = svs.clients + clientnum;
+       client = svs.connectedclients[clientnum];
 
 // set up the client_t
        if (sv.loadgame)
@@ -319,7 +323,7 @@ void SV_ConnectClient (int clientnum, netconn_t *netconnection)
 
        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;
@@ -904,7 +908,7 @@ void SV_PrepareEntitiesForSending(void)
                // 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;
@@ -919,7 +923,7 @@ static int sv_writeentitiestoclient_culled_portal;
 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;
@@ -1066,6 +1070,19 @@ void SV_WriteEntitiesToClient(client_t *client, edict_t *clent, sizebuf_t *msg)
        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;
 
@@ -1098,23 +1115,83 @@ void SV_WriteEntitiesToClient(client_t *client, edict_t *clent, sizebuf_t *msg)
        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);
@@ -1361,10 +1438,10 @@ void SV_UpdateToReliableMessages (void)
        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;
@@ -1388,9 +1465,9 @@ void SV_UpdateToReliableMessages (void)
                        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);
@@ -1400,9 +1477,9 @@ void SV_UpdateToReliableMessages (void)
                        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);
@@ -1412,9 +1489,9 @@ void SV_UpdateToReliableMessages (void)
                        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);
@@ -1424,12 +1501,9 @@ void SV_UpdateToReliableMessages (void)
                }
        }
 
-       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);
 }
@@ -1472,12 +1546,12 @@ void SV_SendClientMessages (void)
        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;
@@ -1509,16 +1583,6 @@ void SV_SendClientMessages (void)
                        }
                }
 
-               // 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))
@@ -1594,7 +1658,7 @@ void SV_CreateBaseline (void)
 
                if (svent->e->free)
                        continue;
-               if (entnum > svs.maxclients && !svent->v->modelindex)
+               if (entnum > MAX_SCOREBOARD && !svent->v->modelindex)
                        continue;
 
                // create entity baseline
@@ -1602,7 +1666,7 @@ void SV_CreateBaseline (void)
                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");
@@ -1685,9 +1749,9 @@ void SV_SaveSpawnparms (void)
 
        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
@@ -1719,7 +1783,7 @@ void SV_IncreaseEdicts(void)
        }
        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 *));
@@ -1768,9 +1832,9 @@ void SV_SpawnServer (const char *server)
 // 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
@@ -1795,7 +1859,7 @@ void SV_SpawnServer (const char *server)
 
 // 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
@@ -1828,7 +1892,7 @@ void SV_SpawnServer (const char *server)
        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;
@@ -1921,9 +1985,9 @@ void SV_SpawnServer (const char *server)
 #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);
index 76959a5..febfea8 100644 (file)
--- a/sv_phys.c
+++ b/sv_phys.c
@@ -1100,25 +1100,6 @@ void SV_Physics_Follow (edict_t *ent)
        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);
-}
-
 /*
 ==============================================================================
 
@@ -1333,9 +1314,9 @@ void SV_Physics (void)
                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
@@ -1361,17 +1342,15 @@ void SV_Physics (void)
                        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);
@@ -1393,7 +1372,7 @@ void SV_Physics (void)
                        SV_Physics_Toss (ent);
                        break;
                case MOVETYPE_FLY:
-                       if (i > 0 && i <= svs.maxclients)
+                       if (i > 0 && i <= MAX_SCOREBOARD)
                        {
                                if (SV_RunThink (ent))
                                {
@@ -1409,7 +1388,7 @@ void SV_Physics (void)
                        break;
                }
 
-               if (i > 0 && i <= svs.maxclients && !ent->e->free)
+               if (i > 0 && i <= MAX_SCOREBOARD && !ent->e->free)
                {
                        SV_CheckVelocity (ent);
 
index 23c31cb..8e4999b 100644 (file)
--- a/sv_user.c
+++ b/sv_user.c
@@ -475,9 +475,9 @@ void SV_ReadClientMove (usercmd_t *move)
                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);
 
@@ -517,14 +517,14 @@ SV_ReadClientMessage
 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);
@@ -595,7 +595,10 @@ void SV_ReadClientMessage(void)
                        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;
                }
        }
@@ -610,9 +613,9 @@ void SV_RunClients (void)
 {
        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;