From: havoc Date: Wed, 6 Aug 2003 13:16:48 +0000 (+0000) Subject: upgraded network protocol to DPPROTOCOL_VERSION4 - this means partial entity updates... X-Git-Tag: xonotic-v0.1.0preview~6459 X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=commitdiff_plain;h=a0338ffd578afdaf693aee96bc6f948afb5b1844 upgraded network protocol to DPPROTOCOL_VERSION4 - this means partial entity updates are now supported (limited bandwidth entity updates), although the "rate" cvar stuff has not been implemented yet... it also means entirely dynamic resizing of arrays in the server's entity database per client, so memory usage goes *WAY* down (used to be 256mb for 64 players, no matter what map/mod, now entirely adaptive as the game goes) 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 --- diff --git a/cl_input.c b/cl_input.c index f92087c5..6463fed5 100644 --- a/cl_input.c +++ b/cl_input.c @@ -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); } diff --git a/cl_main.c b/cl_main.c index 18503b6e..368a78fc 100644 --- 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; diff --git a/cl_parse.c b/cl_parse.c index f0beef03..c8ac50b9 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -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; diff --git a/client.h b/client.h index 4713f19d..c0350f87 100644 --- 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; diff --git a/common.c b/common.c index c4461231..b3378939 100644 --- 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(); diff --git a/gl_rmain.c b/gl_rmain.c index 91f2316e..8c24694b 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -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 3bd6992f..2ca9ade9 100644 --- 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 ; ispawned) { - 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 ; iactive && 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 ; iactive) + 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 ; iactive) + 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 ; iactive) + if (!(client = svs.connectedclients[i])) continue; total = 0; for (j=0 ; j 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 ; iv->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 ; ispawn_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 ; imessage, 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 b00ba289..d7fe96af 100644 --- 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; } diff --git a/netconn.c b/netconn.c index c7dc5336..559203ec 100755 --- 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++) diff --git a/netconn.h b/netconn.h index cfde20de..2e9d9234 100755 --- 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); diff --git a/pr_cmds.c b/pr_cmds.c index 72c72335..956e01e9 100644 --- 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 ; jactive || 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; diff --git a/pr_edict.c b/pr_edict.c index 9f22b1e4..2e487782 100644 --- a/pr_edict.c +++ b/pr_edict.c @@ -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 ; ialpha = 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; } + diff --git a/protocol.h b/protocol.h index 3950fa49..9ec20114 100644 --- a/protocol.h +++ b/protocol.h @@ -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 6b978df8..0f39e371 100644 --- 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 (); } diff --git a/server.h b/server.h index aa2493fa..ed1c89f2 100644 --- 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 diff --git a/sv_main.c b/sv_main.c index 4421ae8d..d96e1196 100644 --- 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 ; ispawned) + 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 ; jactive || !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 ; jactive || !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 ; jactive || !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 ; jactive) - 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 ; iactive) + 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 ; iactive) + 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 ; iactive) - 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); diff --git a/sv_phys.c b/sv_phys.c index 76959a5b..febfea83 100644 --- 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); diff --git a/sv_user.c b/sv_user.c index 23c31cbc..8e4999b9 100644 --- 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 ; iactive) + if (!(host_client = svs.connectedclients[i])) continue; sv_player = host_client->edict;