]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
sv.datagram is now flushed to client->unreliablemsg buffers, along with
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 16 Apr 2007 07:57:51 +0000 (07:57 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 16 Apr 2007 07:57:51 +0000 (07:57 +0000)
logging of good split points so that each effect can be issued to
different packets as space allows (sending some each packet), this makes
effects finally obey the rate limit
cleaned up sending of csqc stats in pre-DP6 protocols and renamed the
autosentstats stuff to customstats to better represent what it does
implemented stats updates in pre-DP6 protocols (mostly for sake of
customstats code cleanlyness)

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

host.c
protocol.c
protocol.h
prvm_exec.c
server.h
sv_main.c
svvm_cmds.c

diff --git a/host.c b/host.c
index 2ce7a994f3ad214e6aa2ae5a5ec6b5b01f5595d9..b81f181c5f818272a56879bc1d3670a2d4a9a357 100644 (file)
--- a/host.c
+++ b/host.c
@@ -734,9 +734,6 @@ void Host_Main(void)
                                // send all messages to the clients
                                SV_SendClientMessages();
 
-                               // clear the general datagram
-                               SV_ClearDatagram();
-
                                // if this server frame took too long, break out of the loop
                                if (framelimit > 1 && Sys_DoubleTime() >= aborttime)
                                        break;
index 9786497eaa9bfdcffe64daad05d9cd9105f9e43a..2b5c8197ecd44cb3069097a9ec81d9e5f35696e1 100644 (file)
@@ -419,6 +419,56 @@ void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int numstates, const entity_sta
        sv2csqcbuf = NULL;
 }
 
+void Protocol_UpdateClientStats(const int *stats)
+{
+       int i;
+       // update the stats array and set deltabits for any changed stats
+       for (i = 0;i < MAX_CL_STATS;i++)
+       {
+               if (host_client->stats[i] != stats[i])
+               {
+                       host_client->statsdeltabits[i >> 3] |= 1 << (i & 7);
+                       host_client->stats[i] = stats[i];
+               }
+       }
+}
+
+void Protocol_WriteStatsReliable(void)
+{
+       int i;
+       if (!host_client->netconnection)
+               return;
+       // detect changes in stats and write reliable messages
+       for (i = 0;i < MAX_CL_STATS;i++)
+       {
+               // quickly skip zero bytes
+               if (!host_client->statsdeltabits[i >> 3])
+               {
+                       i |= 7;
+                       continue;
+               }
+               // check if this bit is set
+               if (host_client->statsdeltabits[i >> 3] & (1 << (i & 7)))
+               {
+                       host_client->statsdeltabits[i >> 3] -= (1 << (i & 7));
+                       // send the stat as a byte if possible
+                       if (host_client->stats[i] >= 0 && host_client->stats[i] < 256)
+                       {
+                               MSG_WriteByte(&host_client->netconnection->message, svc_updatestatubyte);
+                               MSG_WriteByte(&host_client->netconnection->message, i);
+                               MSG_WriteByte(&host_client->netconnection->message, host_client->stats[i]);
+                       }
+                       else
+                       {
+                               MSG_WriteByte(&host_client->netconnection->message, svc_updatestat);
+                               MSG_WriteByte(&host_client->netconnection->message, i);
+                               MSG_WriteLong(&host_client->netconnection->message, host_client->stats[i]);
+                       }
+               }
+       }
+}
+
+
 void EntityFrameQuake_WriteFrame(sizebuf_t *msg, int numstates, const entity_state_t *states)
 {
        const entity_state_t *s;
@@ -2184,13 +2234,13 @@ void EntityFrame5_LostFrame(entityframe5_database_t *d, int framenum)
                        for (j = 0;j < MAX_CL_STATS;j++)
                        {
                                for (l = 0;l < (MAX_CL_STATS+7)/8;l++)
-                                       statsdeltabits[l] = p->statsdeltabits[l] & ~d->statsdeltabits[l];
+                                       statsdeltabits[l] = p->statsdeltabits[l] & ~host_client->statsdeltabits[l];
                                for (k = 0, p2 = d->packetlog;k < ENTITYFRAME5_MAXPACKETLOGS;k++, p2++)
                                        if (p2->packetnumber > framenum)
                                                for (l = 0;l < (MAX_CL_STATS+7)/8;l++)
                                                        statsdeltabits[l] = p->statsdeltabits[l] & ~p2->statsdeltabits[l];
                                for (l = 0;l < (MAX_CL_STATS+7)/8;l++)
-                                       d->statsdeltabits[l] |= statsdeltabits[l];
+                                       host_client->statsdeltabits[l] |= statsdeltabits[l];
                        }
                        // delete this packet log as it is now obsolete
                        p->packetnumber = 0;
@@ -2207,7 +2257,7 @@ void EntityFrame5_AckFrame(entityframe5_database_t *d, int framenum)
                        d->packetlog[i].packetnumber = 0;
 }
 
-void EntityFrame5_WriteFrame(sizebuf_t *msg, entityframe5_database_t *d, int numstates, const entity_state_t *states, int viewentnum, int *stats, int movesequence)
+void EntityFrame5_WriteFrame(sizebuf_t *msg, entityframe5_database_t *d, int numstates, const entity_state_t *states, int viewentnum, int movesequence)
 {
        const entity_state_t *n;
        int i, num, l, framenum, packetlognumber, priority;
@@ -2238,16 +2288,6 @@ void EntityFrame5_WriteFrame(sizebuf_t *msg, entityframe5_database_t *d, int num
        buf.data = data;
        buf.maxsize = sizeof(data);
 
-       // detect changes in stats
-       for (i = 0;i < MAX_CL_STATS;i++)
-       {
-               if (d->stats[i] != stats[i])
-               {
-                       d->statsdeltabits[i>>3] |= (1<<(i&7));
-                       d->stats[i] = stats[i];
-               }
-       }
-
        // detect changes in states
        num = 1;
        for (i = 0, n = states;i < numstates;i++, n++)
@@ -2326,21 +2366,21 @@ void EntityFrame5_WriteFrame(sizebuf_t *msg, entityframe5_database_t *d, int num
        {
                for (i = 0;i < MAX_CL_STATS && msg->cursize + 6 + 11 <= msg->maxsize;i++)
                {
-                       if (d->statsdeltabits[i>>3] & (1<<(i&7)))
+                       if (host_client->statsdeltabits[i>>3] & (1<<(i&7)))
                        {
-                               d->statsdeltabits[i>>3] &= ~(1<<(i&7));
+                               host_client->statsdeltabits[i>>3] &= ~(1<<(i&7));
                                packetlog->statsdeltabits[i>>3] |= (1<<(i&7));
-                               if (d->stats[i] >= 0 && d->stats[i] < 256)
+                               if (host_client->stats[i] >= 0 && host_client->stats[i] < 256)
                                {
                                        MSG_WriteByte(msg, svc_updatestatubyte);
                                        MSG_WriteByte(msg, i);
-                                       MSG_WriteByte(msg, d->stats[i]);
+                                       MSG_WriteByte(msg, host_client->stats[i]);
                                }
                                else
                                {
                                        MSG_WriteByte(msg, svc_updatestat);
                                        MSG_WriteByte(msg, i);
-                                       MSG_WriteLong(msg, d->stats[i]);
+                                       MSG_WriteLong(msg, host_client->stats[i]);
                                }
                        }
                }
index 95bab5500b87102ae019e8e63ee0006c43b8a302..fd794ce8e2525fe901536220230d2b415256b304 100644 (file)
@@ -363,6 +363,13 @@ entity_state_t;
 extern entity_state_t defaultstate;
 // reads a quake entity from the network stream
 void EntityFrameQuake_ReadEntity(int bits);
+// checks for stats changes and sets corresponding host_client->statsdeltabits
+// (also updates host_client->stats array)
+void Protocol_UpdateClientStats(const int *stats);
+// writes reliable messages updating stats (not used by DP6 and later
+// protocols which send updates in their WriteFrame function using a different
+// method of reliable messaging)
+void Protocol_WriteStatsReliable(void);
 // writes a list of quake entities to the network stream
 // (or as many will fit)
 void EntityFrameQuake_WriteFrame(sizebuf_t *msg, int numstates, const entity_state_t *states);
@@ -734,10 +741,6 @@ typedef struct entityframe5_database_s
        // (derived from states)
        unsigned char *visiblebits; // [(maxedicts+7)/8]
 
-       // delta compression of stats
-       unsigned char statsdeltabits[(MAX_CL_STATS+7)/8];
-       int stats[MAX_CL_STATS];
-
        // old notes
 
        // this is used to decide which changestates to set each frame
@@ -760,7 +763,7 @@ int EntityState5_DeltaBitsForState(entity_state_t *o, entity_state_t *n);
 void EntityFrame5_CL_ReadFrame(void);
 void EntityFrame5_LostFrame(entityframe5_database_t *d, int framenum);
 void EntityFrame5_AckFrame(entityframe5_database_t *d, int framenum);
-void EntityFrame5_WriteFrame(sizebuf_t *msg, entityframe5_database_t *d, int numstates, const entity_state_t *states, int viewentnum, int *stats, int movesequence);
+void EntityFrame5_WriteFrame(sizebuf_t *msg, entityframe5_database_t *d, int numstates, const entity_state_t *states, int viewentnum, int movesequence);
 
 extern cvar_t developer_networkentities;
 
index 7481114822f751e2948290990a8ca322f7ff11f8..2a6d44aa395aa6a79b1aa2308ce4bfcbdeb5bac2 100644 (file)
@@ -598,4 +598,6 @@ cleanup:
                Con_Printf("PRVM_ExecuteProgram: %s used %i bytes of tempstrings\n", PRVM_GetString(prog->functions[fnum].s_name), vm_tempstringsbuf.cursize - restorevm_tempstringsbuf_cursize);
        // delete tempstrings created by this function
        vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
+
+       SV_FlushBroadcastMessages();
 }
index b977a63a4f45cdbe03490e015c64f84b44ada380..bd2aabb297a8e23231fe4c6b2f072f344a515212 100644 (file)
--- a/server.h
+++ b/server.h
@@ -193,6 +193,15 @@ typedef struct client_s
        entityframe4_database_t *entitydatabase4;
        entityframe5_database_t *entitydatabase5;
 
+       // delta compression of stats
+       unsigned char statsdeltabits[(MAX_CL_STATS+7)/8];
+       int stats[MAX_CL_STATS];
+
+       unsigned char unreliablemsg_data[NET_MAXMESSAGE];
+       sizebuf_t unreliablemsg;
+       int unreliablemsg_splitpoints;
+       int unreliablemsg_splitpoint[NET_MAXMESSAGE/16];
+
        // information on an active download if any
        qfile_t *download_file;
        int download_expectedposition; // next position the client should ack
@@ -337,7 +346,6 @@ void SV_ConnectClient (int clientnum, netconn_t *netconnection);
 void SV_DropClient (qboolean crash);
 
 void SV_SendClientMessages (void);
-void SV_ClearDatagram (void);
 
 void SV_ReadClientMessage(void);
 
@@ -381,6 +389,7 @@ trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const
 
 #define SV_PointSuperContents(point) (SV_Move((point), vec3_origin, vec3_origin, (point), sv_gameplayfix_swiminbmodels.integer ? MOVE_NOMONSTERS : MOVE_WORLDONLY, NULL, 0).startsupercontents)
 
+void SV_FlushBroadcastMessages(void);
 void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats);
 
 void SV_MoveToGoal (void);
index 8ebe87e0ca9e42afc0e257e98ecc2c79678f0fe0..5a09477d1f650c44c290257676fd8e3081c5a61e 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -25,10 +25,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 void SV_VM_Init();
 void SV_VM_Setup();
 
-void VM_AutoSentStats_Clear (void);
+void VM_CustomStats_Clear (void);
 void EntityFrameCSQC_ClearVersions (void);
 void EntityFrameCSQC_InitClientVersions (int client, qboolean clear);
-void VM_SV_WriteAutoSentStats (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats);
+void VM_SV_UpdateCustomStats (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats);
 void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int numstates, const entity_state_t *states);
 
 
@@ -209,6 +209,7 @@ void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count)
                MSG_WriteChar (&sv.datagram, (int)bound(-128, dir[i]*16, 127));
        MSG_WriteByte (&sv.datagram, count);
        MSG_WriteByte (&sv.datagram, color);
+       SV_FlushBroadcastMessages();
 }
 
 /*
@@ -246,6 +247,7 @@ void SV_StartEffect (vec3_t org, int modelindex, int startframe, int framecount,
                MSG_WriteByte (&sv.datagram, framecount);
                MSG_WriteByte (&sv.datagram, framerate);
        }
+       SV_FlushBroadcastMessages();
 }
 
 /*
@@ -325,6 +327,7 @@ void SV_StartSound (prvm_edict_t *entity, int channel, const char *sample, int v
                MSG_WriteByte (&sv.datagram, sound_num);
        for (i = 0;i < 3;i++)
                MSG_WriteCoord (&sv.datagram, entity->fields.server->origin[i]+0.5*(entity->fields.server->mins[i]+entity->fields.server->maxs[i]), sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 /*
@@ -495,6 +498,9 @@ void SV_ConnectClient (int clientnum, netconn_t *netconnection)
        client->edict = PRVM_EDICT_NUM(clientnum+1);
        if (client->netconnection)
                client->netconnection->message.allowoverflow = true;            // we can catch it
+       // prepare the unreliable message buffer
+       client->unreliablemsg.data = client->unreliablemsg_data;
+       client->unreliablemsg.maxsize = sizeof(client->unreliablemsg_data);
        // updated by receiving "rate" command from client
        client->rate = NET_MINRATE;
        // no limits for local player
@@ -534,17 +540,6 @@ FRAME UPDATES
 ===============================================================================
 */
 
-/*
-==================
-SV_ClearDatagram
-
-==================
-*/
-void SV_ClearDatagram (void)
-{
-       SZ_Clear (&sv.datagram);
-}
-
 /*
 =============================================================================
 
@@ -977,7 +972,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
 entity_state_t sendstates[MAX_EDICTS];
 extern int csqc_clientnum;
 
-void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int *stats)
+void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg)
 {
        int i, numsendstates;
        entity_state_t *s;
@@ -1026,13 +1021,22 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
        EntityFrameCSQC_WriteFrame(msg, numsendstates, sendstates);
 
        if (client->entitydatabase5)
-               EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sendstates, client - svs.clients + 1, stats, client->movesequence);
+               EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sendstates, client - svs.clients + 1, client->movesequence);
        else if (client->entitydatabase4)
+       {
                EntityFrame4_WriteFrame(msg, client->entitydatabase4, numsendstates, sendstates);
+               Protocol_WriteStatsReliable();
+       }
        else if (client->entitydatabase)
+       {
                EntityFrame_WriteFrame(msg, client->entitydatabase, numsendstates, sendstates, client - svs.clients + 1);
+               Protocol_WriteStatsReliable();
+       }
        else
+       {
                EntityFrameQuake_WriteFrame(msg, numsendstates, sendstates);
+               Protocol_WriteStatsReliable();
+       }
 }
 
 /*
@@ -1288,6 +1292,45 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
        }
 }
 
+void SV_FlushBroadcastMessages(void)
+{
+       int i;
+       client_t *client;
+       if (sv.datagram.cursize <= 0)
+               return;
+       for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
+       {
+               if (!client->spawned || !client->netconnection || client->unreliablemsg.cursize + sv.datagram.cursize > client->unreliablemsg.maxsize || client->unreliablemsg_splitpoints >= (int)(sizeof(client->unreliablemsg_splitpoint)/sizeof(client->unreliablemsg_splitpoint[0])))
+                       continue;
+               SZ_Write(&client->unreliablemsg, sv.datagram.data, sv.datagram.cursize);
+               client->unreliablemsg_splitpoint[client->unreliablemsg_splitpoints++] = client->unreliablemsg.cursize;
+       }
+       SZ_Clear(&sv.datagram);
+}
+
+void SV_WriteUnreliableMessages(client_t *client, sizebuf_t *msg)
+{
+       // scan the splitpoints to find out how many we can fit in
+       int numsegments, j, split;
+       for (numsegments = 0;numsegments < client->unreliablemsg_splitpoints;numsegments++)
+               if (msg->cursize + client->unreliablemsg_splitpoint[numsegments] > msg->maxsize)
+                       break;
+       if (numsegments > 0)
+       {
+               // some will fit, so add the ones that will fit
+               split = client->unreliablemsg_splitpoint[numsegments-1];
+               SZ_Write(msg, client->unreliablemsg.data, split);
+               // remove the part we sent, keeping any remaining data
+               client->unreliablemsg.cursize -= split;
+               if (client->unreliablemsg.cursize > 0)
+                       memmove(client->unreliablemsg.data, client->unreliablemsg.data + split, client->unreliablemsg.cursize);
+               // adjust remaining splitpoints
+               client->unreliablemsg_splitpoints -= numsegments;
+               for (j = 0;j < client->unreliablemsg_splitpoints;j++)
+                       client->unreliablemsg_splitpoint[j] = client->unreliablemsg_splitpoint[numsegments + j] - split;
+       }
+}
+
 /*
 =======================
 SV_SendClientDatagram
@@ -1345,17 +1388,21 @@ void SV_SendClientDatagram (client_t *client)
 
                // add the client specific data to the datagram
                SV_WriteClientdataToMessage (client, client->edict, &msg, stats);
-               VM_SV_WriteAutoSentStats (client, client->edict, &msg, stats);
-               SV_WriteEntitiesToClient (client, client->edict, &msg, stats);
+               // now update the stats[] array using any registered custom fields
+               VM_SV_UpdateCustomStats (client, client->edict, &msg, stats);
+               // set host_client->statsdeltabits
+               Protocol_UpdateClientStats (stats);
 
-               // expand packet size to allow effects to go over the rate limit
-               // (dropping them is FAR too ugly)
-               msg.maxsize = maxsize2;
+               // add as many queued unreliable messages (effects) as we can fit
+               // limit effects to half of the remaining space
+               msg.maxsize -= (msg.maxsize - msg.cursize) / 2;
+               if (client->unreliablemsg.cursize)
+                       SV_WriteUnreliableMessages (client, &msg);
+
+               msg.maxsize = maxsize;
 
-               // copy the server datagram if there is space
-               // FIXME: put in delayed queue of effects to send
-               if (sv.datagram.cursize > 0 && msg.cursize + sv.datagram.cursize <= msg.maxsize)
-                       SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize);
+               // now write as many entities as we can fit, and also sends stats
+               SV_WriteEntitiesToClient (client, client->edict, &msg);
        }
        else if (realtime > client->keepalivetime)
        {
@@ -1498,6 +1545,8 @@ void SV_SendClientMessages (void)
        if (sv.protocol == PROTOCOL_QUAKEWORLD)
                Sys_Error("SV_SendClientMessages: no quakeworld support\n");
 
+       SV_FlushBroadcastMessages();
+
 // update frags, names, etc
        SV_UpdateToReliableMessages();
 
@@ -2649,7 +2698,7 @@ void SV_VM_Setup(void)
        // OP_STATE is always supported on server (due to entvars_t)
        prog->flag |= PRVM_OP_STATE;
 
-       VM_AutoSentStats_Clear();//[515]: csqc
+       VM_CustomStats_Clear();//[515]: csqc
        EntityFrameCSQC_ClearVersions();//[515]: csqc
 
        PRVM_End;
index cfe61808d6654f1899b9ba408990a5a9487d7bbb..6e8017ff3247a11ff7cca36623beed92b3d53d06 100644 (file)
@@ -1309,102 +1309,51 @@ typedef struct
 {
        unsigned char   type;   // 1/2/8 or other value if isn't used
        int             fieldoffset;
-}autosentstat_t;
+}customstat_t;
 
-static autosentstat_t *vm_autosentstats = NULL;        //[515]: it starts from 0, not 32
-static int vm_autosentstats_last;
+static customstat_t *vm_customstats = NULL;    //[515]: it starts from 0, not 32
+static int vm_customstats_last;
 
-void VM_AutoSentStats_Clear (void)
+void VM_CustomStats_Clear (void)
 {
-       if(vm_autosentstats)
+       if(vm_customstats)
        {
-               Z_Free(vm_autosentstats);
-               vm_autosentstats = NULL;
-               vm_autosentstats_last = -1;
+               Z_Free(vm_customstats);
+               vm_customstats = NULL;
+               vm_customstats_last = -1;
        }
 }
 
-//[515]: add check if even bigger ? "try to use two stats, cause it's too big" ?
-#define VM_SENDSTAT(a,b,c)\
-{\
-/*     if((c))*/\
-       if((c)==(unsigned char)(c))\
-       {\
-               MSG_WriteByte((a), svc_updatestatubyte);\
-               MSG_WriteByte((a), (b));\
-               MSG_WriteByte((a), (c));\
-       }\
-       else\
-       {\
-               MSG_WriteByte((a), svc_updatestat);\
-               MSG_WriteByte((a), (b));\
-               MSG_WriteLong((a), (c));\
-       }\
-}\
-
-void VM_SV_WriteAutoSentStats (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats)
+void VM_SV_UpdateCustomStats (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats)
 {
-       int                     i, v, *si;
+       int                     i;
        char            s[17];
-       const char      *t;
-       qboolean        send;
-       union
-       {
-               float   f;
-               int             i;
-       }k;
 
-       if(!vm_autosentstats)
+       if(!vm_customstats)
                return;
 
-       send = (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_DARKPLACES1 && sv.protocol != PROTOCOL_DARKPLACES2 && sv.protocol != PROTOCOL_DARKPLACES3 && sv.protocol != PROTOCOL_DARKPLACES4 && sv.protocol != PROTOCOL_DARKPLACES5);
-
-       for(i=0; i<vm_autosentstats_last+1 ;i++)
+       for(i=0; i<vm_customstats_last+1 ;i++)
        {
-               if(!vm_autosentstats[i].type)
+               if(!vm_customstats[i].type)
                        continue;
-               switch(vm_autosentstats[i].type)
+               switch(vm_customstats[i].type)
                {
-               //string
+               //string as 16 bytes
                case 1:
-                       t = PRVM_E_STRING(ent, vm_autosentstats[i].fieldoffset);
-                       if(t && t[0])
-                       {
-                               memset(s, 0, 17);
-                               strlcpy(s, t, 16);
-                               si = (int*)s;
-                               if (!send)
-                               {
-                                       stats[i+32] = si[0];
-                                       stats[i+33] = si[1];
-                                       stats[i+34] = si[2];
-                                       stats[i+35] = si[3];
-                               }
-                               else
-                               {
-                                       VM_SENDSTAT(msg, i+32, si[0]);
-                                       VM_SENDSTAT(msg, i+33, si[1]);
-                                       VM_SENDSTAT(msg, i+34, si[2]);
-                                       VM_SENDSTAT(msg, i+35, si[3]);
-                               }
-                       }
+                       memset(s, 0, 17);
+                       strlcpy(s, PRVM_E_STRING(ent, vm_customstats[i].fieldoffset), 16);
+                       stats[i+32] = s[ 0] + s[ 1] * 256 + s[ 2] * 65536 + s[ 3] * 16777216;
+                       stats[i+33] = s[ 4] + s[ 5] * 256 + s[ 6] * 65536 + s[ 7] * 16777216;
+                       stats[i+34] = s[ 8] + s[ 9] * 256 + s[10] * 65536 + s[11] * 16777216;
+                       stats[i+35] = s[12] + s[13] * 256 + s[14] * 65536 + s[15] * 16777216;
                        break;
-               //float
+               //float field sent as-is
                case 2:
-                       k.f = PRVM_E_FLOAT(ent, vm_autosentstats[i].fieldoffset);       //[515]: use PRVM_E_INT ?
-                       k.i = LittleLong (k.i);
-                       if (!send)
-                               stats[i+32] = k.i;
-                       else
-                               VM_SENDSTAT(msg, i+32, k.i);
+                       stats[i+32] = PRVM_E_INT(ent, vm_customstats[i].fieldoffset);
                        break;
-               //integer
+               //integer value of float field
                case 8:
-                       v = (int)PRVM_E_FLOAT(ent, vm_autosentstats[i].fieldoffset);    //[515]: use PRVM_E_INT ?
-                       if (!send)
-                               stats[i+32] = v;
-                       else
-                               VM_SENDSTAT(msg, i+32, v);
+                       stats[i+32] = (int)PRVM_E_FLOAT(ent, vm_customstats[i].fieldoffset);
                        break;
                default:
                        break;
@@ -1426,10 +1375,10 @@ static void VM_SV_AddStat (void)
 
        VM_SAFEPARMCOUNT(3, VM_SV_AddStat);
 
-       if(!vm_autosentstats)
+       if(!vm_customstats)
        {
-               vm_autosentstats = (autosentstat_t *)Z_Malloc((MAX_CL_STATS-32) * sizeof(autosentstat_t));
-               if(!vm_autosentstats)
+               vm_customstats = (customstat_t *)Z_Malloc((MAX_CL_STATS-32) * sizeof(customstat_t));
+               if(!vm_customstats)
                {
                        VM_Warning("PF_SV_AddStat: not enough memory\n");
                        return;
@@ -1455,10 +1404,10 @@ static void VM_SV_AddStat (void)
                VM_Warning("PF_SV_AddStat: index > (MAX_CL_STATS-4) with string\n");
                return;
        }
-       vm_autosentstats[i].type                = type;
-       vm_autosentstats[i].fieldoffset = off;
-       if(vm_autosentstats_last < i)
-               vm_autosentstats_last = i;
+       vm_customstats[i].type          = type;
+       vm_customstats[i].fieldoffset   = off;
+       if(vm_customstats_last < i)
+               vm_customstats_last = i;
 }
 
 /*
@@ -1602,6 +1551,7 @@ static void VM_SV_te_blood (void)
        MSG_WriteByte(&sv.datagram, bound(-128, (int) PRVM_G_VECTOR(OFS_PARM1)[2], 127));
        // count
        MSG_WriteByte(&sv.datagram, bound(0, (int) PRVM_G_FLOAT(OFS_PARM2), 255));
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_bloodshower (void)
@@ -1623,6 +1573,7 @@ static void VM_SV_te_bloodshower (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_FLOAT(OFS_PARM2), sv.protocol);
        // count
        MSG_WriteShort(&sv.datagram, (int)bound(0, PRVM_G_FLOAT(OFS_PARM3), 65535));
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_explosionrgb (void)
@@ -1638,6 +1589,7 @@ static void VM_SV_te_explosionrgb (void)
        MSG_WriteByte(&sv.datagram, bound(0, (int) (PRVM_G_VECTOR(OFS_PARM1)[0] * 255), 255));
        MSG_WriteByte(&sv.datagram, bound(0, (int) (PRVM_G_VECTOR(OFS_PARM1)[1] * 255), 255));
        MSG_WriteByte(&sv.datagram, bound(0, (int) (PRVM_G_VECTOR(OFS_PARM1)[2] * 255), 255));
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_particlecube (void)
@@ -1667,6 +1619,7 @@ static void VM_SV_te_particlecube (void)
        MSG_WriteByte(&sv.datagram, ((int) PRVM_G_FLOAT(OFS_PARM5)) != 0);
        // randomvel
        MSG_WriteCoord(&sv.datagram, PRVM_G_FLOAT(OFS_PARM6), sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_particlerain (void)
@@ -1692,6 +1645,7 @@ static void VM_SV_te_particlerain (void)
        MSG_WriteShort(&sv.datagram, (int)bound(0, PRVM_G_FLOAT(OFS_PARM3), 65535));
        // color
        MSG_WriteByte(&sv.datagram, (int)PRVM_G_FLOAT(OFS_PARM4));
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_particlesnow (void)
@@ -1717,6 +1671,7 @@ static void VM_SV_te_particlesnow (void)
        MSG_WriteShort(&sv.datagram, (int)bound(0, PRVM_G_FLOAT(OFS_PARM3), 65535));
        // color
        MSG_WriteByte(&sv.datagram, (int)PRVM_G_FLOAT(OFS_PARM4));
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_spark (void)
@@ -1736,6 +1691,7 @@ static void VM_SV_te_spark (void)
        MSG_WriteByte(&sv.datagram, bound(-128, (int) PRVM_G_VECTOR(OFS_PARM1)[2], 127));
        // count
        MSG_WriteByte(&sv.datagram, bound(0, (int) PRVM_G_FLOAT(OFS_PARM2), 255));
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_gunshotquad (void)
@@ -1747,6 +1703,7 @@ static void VM_SV_te_gunshotquad (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_spikequad (void)
@@ -1758,6 +1715,7 @@ static void VM_SV_te_spikequad (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_superspikequad (void)
@@ -1769,6 +1727,7 @@ static void VM_SV_te_superspikequad (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_explosionquad (void)
@@ -1780,6 +1739,7 @@ static void VM_SV_te_explosionquad (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_smallflash (void)
@@ -1791,6 +1751,7 @@ static void VM_SV_te_smallflash (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_customflash (void)
@@ -1812,6 +1773,7 @@ static void VM_SV_te_customflash (void)
        MSG_WriteByte(&sv.datagram, (int)bound(0, PRVM_G_VECTOR(OFS_PARM3)[0] * 255, 255));
        MSG_WriteByte(&sv.datagram, (int)bound(0, PRVM_G_VECTOR(OFS_PARM3)[1] * 255, 255));
        MSG_WriteByte(&sv.datagram, (int)bound(0, PRVM_G_VECTOR(OFS_PARM3)[2] * 255, 255));
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_gunshot (void)
@@ -1823,6 +1785,7 @@ static void VM_SV_te_gunshot (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_spike (void)
@@ -1834,6 +1797,7 @@ static void VM_SV_te_spike (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_superspike (void)
@@ -1845,6 +1809,7 @@ static void VM_SV_te_superspike (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_explosion (void)
@@ -1856,6 +1821,7 @@ static void VM_SV_te_explosion (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_tarexplosion (void)
@@ -1867,6 +1833,7 @@ static void VM_SV_te_tarexplosion (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_wizspike (void)
@@ -1878,6 +1845,7 @@ static void VM_SV_te_wizspike (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_knightspike (void)
@@ -1889,6 +1857,7 @@ static void VM_SV_te_knightspike (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_lavasplash (void)
@@ -1900,6 +1869,7 @@ static void VM_SV_te_lavasplash (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_teleport (void)
@@ -1911,6 +1881,7 @@ static void VM_SV_te_teleport (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_explosion2 (void)
@@ -1925,6 +1896,7 @@ static void VM_SV_te_explosion2 (void)
        // color
        MSG_WriteByte(&sv.datagram, (int)PRVM_G_FLOAT(OFS_PARM1));
        MSG_WriteByte(&sv.datagram, (int)PRVM_G_FLOAT(OFS_PARM2));
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_lightning1 (void)
@@ -1942,6 +1914,7 @@ static void VM_SV_te_lightning1 (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_lightning2 (void)
@@ -1959,6 +1932,7 @@ static void VM_SV_te_lightning2 (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_lightning3 (void)
@@ -1976,6 +1950,7 @@ static void VM_SV_te_lightning3 (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_beam (void)
@@ -1993,6 +1968,7 @@ static void VM_SV_te_beam (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_plasmaburn (void)
@@ -2003,6 +1979,7 @@ static void VM_SV_te_plasmaburn (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[0], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol);
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 static void VM_SV_te_flamejet (void)
@@ -2020,6 +1997,7 @@ static void VM_SV_te_flamejet (void)
        MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM1)[2], sv.protocol);
        // count
        MSG_WriteByte(&sv.datagram, (int)PRVM_G_FLOAT(OFS_PARM2));
+       SV_FlushBroadcastMessages();
 }
 
 void clippointtosurface(model_t *model, msurface_t *surface, vec3_t p, vec3_t out)
@@ -2658,6 +2636,7 @@ static void VM_SV_trailparticles (void)
        MSG_WriteShort(&sv.datagram, (int)PRVM_G_FLOAT(OFS_PARM1));
        MSG_WriteVector(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2), sv.protocol);
        MSG_WriteVector(&sv.datagram, PRVM_G_VECTOR(OFS_PARM3), sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 //#337 void(float effectnum, vector origin, vector dir, float count) pointparticles (EXT_CSQC)
@@ -2670,6 +2649,7 @@ static void VM_SV_pointparticles (void)
        MSG_WriteVector(&sv.datagram, PRVM_G_VECTOR(OFS_PARM1), sv.protocol);
        MSG_WriteVector(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2), sv.protocol);
        MSG_WriteShort(&sv.datagram, bound(0, (int)PRVM_G_FLOAT(OFS_PARM3), 65535));
+       SV_FlushBroadcastMessages();
 }
 
 prvm_builtin_t vm_sv_builtins[] = {