// sv_main.c -- server main program #include "qwsvdef.h" unsigned clients_multicast; #define CHAN_AUTO 0 #define CHAN_WEAPON 1 #define CHAN_VOICE 2 #define CHAN_ITEM 3 #define CHAN_BODY 4 #define PHS_OVERRIDE_R 8 /* ============================================================================= Con_Printf redirection ============================================================================= */ char outputbuf[8000]; redirect_t sv_redirected; extern cvar_t sv_phs; extern cvar_t sv_namedistance; /* ================== SV_FlushRedirect ================== */ void SV_FlushRedirect (void) { char send[8000+6]; if (sv_redirected == RD_PACKET) { send[0] = 0xff; send[1] = 0xff; send[2] = 0xff; send[3] = 0xff; send[4] = A2C_PRINT; memcpy (send+5, outputbuf, strlen(outputbuf)+1); NET_SendPacket (strlen(send)+1, send, net_from); } else if (sv_redirected == RD_CLIENT) { MSG_WriteByte (&host_client->netchan.message, svc_print); MSG_WriteByte (&host_client->netchan.message, PRINT_HIGH); MSG_WriteString (&host_client->netchan.message, outputbuf); } // clear it outputbuf[0] = 0; } /* ================== SV_BeginRedirect Send Con_Printf data to the remote client instead of the console ================== */ void SV_BeginRedirect (redirect_t rd) { sv_redirected = rd; outputbuf[0] = 0; } void SV_EndRedirect (void) { SV_FlushRedirect (); sv_redirected = RD_NONE; } /* ================ Con_Printf Handles cursor positioning, line wrapping, etc ================ */ #define MAXPRINTMSG 4096 // FIXME: make a buffer size safe vsprintf? void Con_Printf (char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; va_start (argptr,fmt); vsprintf (msg,fmt,argptr); va_end (argptr); // add to redirected message if (sv_redirected) { if (strlen (msg) + strlen(outputbuf) > sizeof(outputbuf) - 1) SV_FlushRedirect (); strcat (outputbuf, msg); return; } Sys_Printf ("%s", msg); // also echo to debugging console if (sv_logfile) fprintf (sv_logfile, "%s", msg); } /* ================ Con_DPrintf A Con_Printf that only shows up if the "developer" cvar is set ================ */ void Con_DPrintf (char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; if (!developer.value) return; va_start (argptr,fmt); vsprintf (msg,fmt,argptr); va_end (argptr); Con_Printf ("%s", msg); } /* ============================================================================= EVENT MESSAGES ============================================================================= */ /* ================= SV_ClientPrintf Sends text across to be displayed if the level passes ================= */ void SV_ClientPrintf (client_t *cl, int level, char *fmt, ...) { va_list argptr; char string[1024]; if (level < cl->messagelevel) return; va_start (argptr,fmt); vsprintf (string, fmt,argptr); va_end (argptr); MSG_WriteByte (&cl->netchan.message, svc_print); MSG_WriteByte (&cl->netchan.message, level); MSG_WriteString (&cl->netchan.message, string); } /* ================= SV_BroadcastPrintf Sends text to all active clients ================= */ void SV_BroadcastPrintf (int level, char *fmt, ...) { va_list argptr; char string[1024]; client_t *cl; int i; va_start (argptr,fmt); vsprintf (string, fmt,argptr); va_end (argptr); Sys_Printf ("%s", string); // print to the console for (i=0, cl = svs.clients ; imessagelevel) continue; if (!cl->state) continue; MSG_WriteByte (&cl->netchan.message, svc_print); MSG_WriteByte (&cl->netchan.message, level); MSG_WriteString (&cl->netchan.message, string); } } /* ================= SV_BroadcastCommand Sends text to all active clients ================= */ void SV_BroadcastCommand (char *fmt, ...) { va_list argptr; char string[1024]; if (!sv.state) return; va_start (argptr,fmt); vsprintf (string, fmt,argptr); va_end (argptr); MSG_WriteByte (&sv.reliable_datagram, svc_stufftext); MSG_WriteString (&sv.reliable_datagram, string); } /* ================= SV_Multicast Sends the contents of sv.multicast to a subset of the clients, then clears sv.multicast. MULTICAST_ALL same as broadcast MULTICAST_PVS send to clients potentially visible from org MULTICAST_PHS send to clients potentially hearable from org ================= */ void SV_Multicast (vec3_t origin, int to) { client_t *client; byte *mask; mleaf_t *leaf; int leafnum; int j; qboolean reliable; vec3_t adjust_origin; clients_multicast = 0; leaf = Mod_PointInLeaf (origin, sv.worldmodel); if (!leaf) leafnum = 0; else leafnum = leaf - sv.worldmodel->leafs; reliable = false; switch (to) { case MULTICAST_ALL_R: reliable = true; // intentional fallthrough case MULTICAST_ALL: mask = sv.pvs; // leaf 0 is everything; break; case MULTICAST_PHS_R: reliable = true; // intentional fallthrough case MULTICAST_PHS: mask = sv.phs + leafnum * 4*((sv.worldmodel->numleafs+31)>>5); break; case MULTICAST_PVS_R: reliable = true; // intentional fallthrough case MULTICAST_PVS: mask = sv.pvs + leafnum * 4*((sv.worldmodel->numleafs+31)>>5); break; default: mask = NULL; SV_Error ("SV_Multicast: bad to:%i", to); } // send the data to all relevent clients for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { if (client->state != cs_spawned) continue; VectorCopy(client->edict->v.origin, adjust_origin); adjust_origin[2] += 16; leaf = Mod_PointInLeaf (adjust_origin, sv.worldmodel); if (leaf)// && leaf != sv.worldmodel->leafs) { // -1 is because pvs rows are 1 based, not 0 based like leafs leafnum = leaf - sv.worldmodel->leafs - 1; if ( !(mask[leafnum>>3] & (1<<(leafnum&7)) )) { // Con_Printf ("supressed multicast\n"); if (mask == sv.pvs) Sys_Printf("supressed multicast to all!!!\n"); continue; } } clients_multicast |= 1l << j; if (reliable) SZ_Write (&client->netchan.message, sv.multicast.data, sv.multicast.cursize); else SZ_Write (&client->datagram, sv.multicast.data, sv.multicast.cursize); } SZ_Clear (&sv.multicast); } /* ================= SV_MulticastSpecific Sends the contents of sv.multicast to a subset of the clients, then clears sv.multicast. ================= */ void SV_MulticastSpecific (unsigned clients, qboolean reliable) { client_t *client; int j; clients_multicast = 0; // send the data to all relevent clients for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { if (client->state != cs_spawned) continue; if ((1l << j) & clients) { clients_multicast |= 1l << j; if (reliable) SZ_Write (&client->netchan.message, sv.multicast.data, sv.multicast.cursize); else SZ_Write (&client->datagram, sv.multicast.data, sv.multicast.cursize); } } SZ_Clear (&sv.multicast); } /* ================== SV_StopSound ================== */ void SV_StopSound (edict_t *entity, int channel) { int ent,i; vec3_t origin; ent = NUM_FOR_EDICT(entity); channel = (ent<<3) | channel; // use the entity origin unless it is a bmodel if (entity->v.solid == SOLID_BSP) { for (i=0 ; i<3 ; i++)//FIXME: This may not work- should be using (absmin + absmax)*0.5? origin[i] = entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i]); } else { VectorCopy (entity->v.origin, origin); } MSG_WriteByte (&sv.multicast, svc_stopsound); MSG_WriteShort (&sv.multicast, channel); SV_Multicast (origin, MULTICAST_ALL_R); } /* ================== SV_UpdateSoundPos ================== */ void SV_UpdateSoundPos (edict_t *entity, int channel) { int ent; int i; vec3_t origin; ent = NUM_FOR_EDICT(entity); channel = (ent<<3) | channel; // use the entity origin unless it is a bmodel if (entity->v.solid == SOLID_BSP) { for (i=0 ; i<3 ; i++)//FIXME: This may not work- should be using (absmin + absmax)*0.5? origin[i] = entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i]); } else { VectorCopy (entity->v.origin, origin); } MSG_WriteByte (&sv.multicast, svc_sound_update_pos); MSG_WriteShort (&sv.multicast, channel); for (i=0 ; i<3 ; i++) MSG_WriteCoord (&sv.multicast, entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i])); SV_Multicast (origin, MULTICAST_PHS); } /* ================== SV_StartSound Each entity can have eight independant sound sources, like voice, weapon, feet, etc. Channel 0 is an auto-allocate channel, the others override anything allready running on that entity/channel pair. An attenuation of 0 will play full volume everywhere in the level. Larger attenuations will drop off. (max 4 attenuation) ================== */ void SV_StartSound (edict_t *entity, int channel, char *sample, int volume, float attenuation) { int sound_num; int field_mask; int i; int ent; vec3_t origin; qboolean use_phs; qboolean reliable = false; if (volume < 0 || volume > 255) SV_Error ("SV_StartSound: volume = %i", volume); if (attenuation < 0 || attenuation > 4) SV_Error ("SV_StartSound: attenuation = %f", attenuation); if (channel < 0 || channel > 15) SV_Error ("SV_StartSound: channel = %i", channel); // find precache number for sound for (sound_num=1 ; sound_numv.solid == SOLID_BSP) { for (i=0 ; i<3 ; i++)//FIXME: This may not work- should be using (absmin + absmax)*0.5? origin[i] = entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i]); } else { VectorCopy (entity->v.origin, origin); } MSG_WriteByte (&sv.multicast, svc_sound); MSG_WriteShort (&sv.multicast, channel); if (channel & SND_VOLUME) MSG_WriteByte (&sv.multicast, volume); if (channel & SND_ATTENUATION) MSG_WriteByte (&sv.multicast, attenuation*32); MSG_WriteByte (&sv.multicast, sound_num); for (i=0 ; i<3 ; i++) MSG_WriteCoord (&sv.multicast, origin[i]); if (use_phs) SV_Multicast (origin, reliable ? MULTICAST_PHS_R : MULTICAST_PHS); else SV_Multicast (origin, reliable ? MULTICAST_ALL_R : MULTICAST_ALL); } /* ================== SV_StartParticle Make sure the event gets sent to all clients ================== */ void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count) { int i, v; MSG_WriteByte (&sv.multicast, svc_particle); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); MSG_WriteCoord (&sv.multicast, org[2]); for (i=0 ; i<3 ; i++) { v = dir[i]*16; if (v > 127) v = 127; else if (v < -128) v = -128; MSG_WriteChar (&sv.multicast, v); } MSG_WriteByte (&sv.multicast, count); MSG_WriteByte (&sv.multicast, color); SV_Multicast (org, MULTICAST_PVS); } /* ================== SV_StartParticle2 Make sure the event gets sent to all clients ================== */ void SV_StartParticle2 (vec3_t org, vec3_t dmin, vec3_t dmax, int color, int effect, int count) { MSG_WriteByte (&sv.multicast, svc_particle2); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); MSG_WriteCoord (&sv.multicast, org[2]); MSG_WriteFloat (&sv.multicast, dmin[0]); MSG_WriteFloat (&sv.multicast, dmin[1]); MSG_WriteFloat (&sv.multicast, dmin[2]); MSG_WriteFloat (&sv.multicast, dmax[0]); MSG_WriteFloat (&sv.multicast, dmax[1]); MSG_WriteFloat (&sv.multicast, dmax[2]); MSG_WriteShort (&sv.multicast, color); MSG_WriteByte (&sv.multicast, count); MSG_WriteByte (&sv.multicast, effect); SV_Multicast (org, MULTICAST_PVS); } /* ================== SV_StartParticle3 Make sure the event gets sent to all clients ================== */ void SV_StartParticle3 (vec3_t org, vec3_t box, int color, int effect, int count) { MSG_WriteByte (&sv.multicast, svc_particle3); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); MSG_WriteCoord (&sv.multicast, org[2]); MSG_WriteByte (&sv.multicast, box[0]); MSG_WriteByte (&sv.multicast, box[1]); MSG_WriteByte (&sv.multicast, box[2]); MSG_WriteShort (&sv.multicast, color); MSG_WriteByte (&sv.multicast, count); MSG_WriteByte (&sv.multicast, effect); SV_Multicast (org, MULTICAST_PVS); } /* ================== SV_StartParticle4 Make sure the event gets sent to all clients ================== */ void SV_StartParticle4 (vec3_t org, float radius, int color, int effect, int count) { MSG_WriteByte (&sv.multicast, svc_particle4); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); MSG_WriteCoord (&sv.multicast, org[2]); MSG_WriteByte (&sv.multicast, radius); MSG_WriteShort (&sv.multicast, color); MSG_WriteByte (&sv.multicast, count); MSG_WriteByte (&sv.multicast, effect); SV_Multicast (org, MULTICAST_PVS); } void SV_StartRainEffect (vec3_t org, vec3_t e_size, int x_dir, int y_dir, int color, int count) { MSG_WriteByte (&sv.multicast, svc_raineffect); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); MSG_WriteCoord (&sv.multicast, org[2]); MSG_WriteCoord (&sv.multicast, e_size[0]); MSG_WriteCoord (&sv.multicast, e_size[1]); MSG_WriteCoord (&sv.multicast, e_size[2]); MSG_WriteAngle (&sv.multicast, x_dir); MSG_WriteAngle (&sv.multicast, y_dir); MSG_WriteShort (&sv.multicast, color); MSG_WriteShort (&sv.multicast, count); SV_Multicast (org, MULTICAST_PVS); } /* =============================================================================== FRAME UPDATES =============================================================================== */ //int sv_nailmodel, sv_supernailmodel, sv_playermodel[MAX_PLAYER_CLASS]; int sv_magicmissmodel, sv_playermodel[MAX_PLAYER_CLASS]; int sv_ravenmodel, sv_raven2model; void SV_FindModelNumbers (void) { int i; // sv_nailmodel = -1; // sv_supernailmodel = -1; sv_playermodel[0] = -1; sv_playermodel[1] = -1; sv_playermodel[2] = -1; sv_playermodel[3] = -1; sv_playermodel[4] = -1; sv_magicmissmodel = -1; sv_ravenmodel = -1; sv_raven2model = -1; for (i=0 ; iedict; // send the chokecount for r_netgraph if (client->chokecount) { MSG_WriteByte (msg, svc_chokecount); MSG_WriteByte (msg, client->chokecount); client->chokecount = 0; } // send a damage message if the player got hit this frame if (ent->v.dmg_take || ent->v.dmg_save) { other = PROG_TO_EDICT(ent->v.dmg_inflictor); MSG_WriteByte (msg, svc_damage); MSG_WriteByte (msg, ent->v.dmg_save); MSG_WriteByte (msg, ent->v.dmg_take); for (i=0 ; i<3 ; i++) MSG_WriteCoord (msg, other->v.origin[i] + 0.5*(other->v.mins[i] + other->v.maxs[i])); ent->v.dmg_take = 0; ent->v.dmg_save = 0; } // a fixangle might get lost in a dropped packet. Oh well. if ( ent->v.fixangle ) { MSG_WriteByte (msg, svc_setangle); for (i=0 ; i < 3 ; i++) MSG_WriteAngle (msg, ent->v.angles[i] ); ent->v.fixangle = 0; } // if the player has a target, send its info... if (ent->v.targDist) { MSG_WriteByte(msg, svc_targetupdate); MSG_WriteByte(msg, ent->v.targAng); MSG_WriteByte(msg, ent->v.targPitch); MSG_WriteByte(msg, (ent->v.targDist < 255.0) ? (int)(ent->v.targDist) : 255); } } /* ======================= SV_UpdateClientStats Performs a delta update of the stats array. This should only be performed when a reliable message can be delivered this frame. ======================= */ void SV_UpdateClientStats (client_t *client) { edict_t *ent; int stats[MAX_CL_STATS]; int i; ent = client->edict; memset (stats, 0, sizeof(stats)); // if we are a spectator and we are tracking a player, we get his stats // so our status bar reflects his if (client->spectator && client->spec_track > 0) ent = svs.clients[client->spec_track - 1].edict; stats[STAT_HEALTH] = 0;//ent->v.health; stats[STAT_WEAPON] = SV_ModelIndex(pr_strings+ent->v.weaponmodel); stats[STAT_AMMO] = 0;//ent->v.currentammo; stats[STAT_ARMOR] = 0;//ent->v.armorvalue; stats[STAT_SHELLS] = 0;//ent->v.ammo_shells; stats[STAT_NAILS] = 0;//ent->v.ammo_nails; stats[STAT_ROCKETS] = 0;//ent->v.ammo_rockets; stats[STAT_CELLS] = 0;//ent->v.ammo_cells; if (!client->spectator) stats[STAT_ACTIVEWEAPON] = 0;//ent->v.weapon; // stuff the sigil bits into the high bits of items for sbar stats[STAT_ITEMS] = 0;//(int)ent->v.items | ((int)pr_global_struct->serverflags << 28); for (i=0 ; istats[i]) { client->stats[i] = stats[i]; if (stats[i] >=0 && stats[i] <= 255) { MSG_WriteByte (&client->netchan.message, svc_updatestat); MSG_WriteByte (&client->netchan.message, i); MSG_WriteByte (&client->netchan.message, stats[i]); } else { MSG_WriteByte (&client->netchan.message, svc_updatestatlong); MSG_WriteByte (&client->netchan.message, i); MSG_WriteLong (&client->netchan.message, stats[i]); } } } /* ======================= SV_SendClientDatagram ======================= */ qboolean SV_SendClientDatagram (client_t *client) { byte buf[MAX_DATAGRAM]; sizebuf_t msg; msg.data = buf; msg.maxsize = sizeof(buf); msg.cursize = 0; msg.allowoverflow = true; msg.overflowed = false; // add the client specific data to the datagram SV_WriteClientdataToMessage (client, &msg); // send over all the objects that are in the PVS // this will include clients, a packetentities, and // possibly a nails update SV_WriteEntitiesToClient (client, &msg); // copy the accumulated multicast datagram // for this client out to the message if (client->datagram.overflowed) Con_Printf ("WARNING: datagram overflowed for %s\n", client->name); else SZ_Write (&msg, client->datagram.data, client->datagram.cursize); SZ_Clear (&client->datagram); // send deltas over reliable stream if (Netchan_CanReliable (&client->netchan)) SV_UpdateClientStats (client); if (msg.overflowed) { Con_Printf ("WARNING: msg overflowed for %s\n", client->name); SZ_Clear (&msg); } // send the datagram Netchan_Transmit (&client->netchan, msg.cursize, buf); return true; } static qboolean ValidToShowName(edict_t *edict) { if (edict->v.deadflag) return false; if((int)edict->v.effects & EF_NODRAW) return false; return true; } static void UpdatePIV(void) { int i, j; client_t *client; trace_t trace; vec3_t adjust_org1, adjust_org2, distvec; float save_hull, dist; for (i=0, host_client = svs.clients ; iPIV = 0; } for (i=0, host_client = svs.clients ; istate != cs_spawned || host_client->spectator) continue; VectorCopy(host_client->edict->v.origin, adjust_org1); adjust_org1[2] += 24; save_hull = host_client->edict->v.hull; host_client->edict->v.hull = 0; for(j=i+1, client = host_client+1 ; jstate != cs_spawned || client->spectator) continue; VectorSubtract(client->edict->v.origin, host_client->edict->v.origin, distvec); dist = VectorNormalize(distvec); if(dist > sv_namedistance.value) { // Con_Printf("dist %f\n", dist); continue; } VectorCopy(client->edict->v.origin, adjust_org2); adjust_org2[2] += 24; trace = SV_Move (adjust_org1, vec3_origin, vec3_origin, adjust_org2, false, host_client->edict); if (trace.ent == client->edict) { //can see each other, check for invisible, dead if (ValidToShowName(client->edict)) host_client->PIV |= 1<edict)) client->PIV |= 1<edict->v.hull = save_hull; } } /* ======================= SV_UpdateToReliableMessages ======================= */ void SV_UpdateToReliableMessages (void) { int i, j; client_t *client; eval_t *val; edict_t *ent; qboolean CheckPIV = false; // Con_Printf("SV_UpdateToReliableMessages\n"); if (sv.time - sv.next_PIV_time >= 1) { sv.next_PIV_time = sv.time + 1; CheckPIV = true; UpdatePIV(); } // check for changes to be sent over the reliable streams to all clients for (i=0, host_client = svs.clients ; istate != cs_spawned) continue; if (host_client->sendinfo) { host_client->sendinfo = false; SV_FullClientUpdate (host_client, &sv.reliable_datagram); } if (host_client->old_frags != host_client->edict->v.frags) { for (j=0, client = svs.clients ; jstate < cs_connected) continue; //Sys_Printf("SV_UpdateToReliableMessages: Updated frags for client %d to %d\n", i, j); MSG_WriteByte (&client->netchan.message, svc_updatedminfo); MSG_WriteByte (&client->netchan.message, i); MSG_WriteShort (&client->netchan.message, host_client->edict->v.frags); MSG_WriteByte (&client->netchan.message, (host_client->playerclass<<5)|((int)host_client->edict->v.level&31)); if(dmMode.value==DM_SIEGE) { MSG_WriteByte (&client->netchan.message, svc_updatesiegelosses); MSG_WriteByte (&client->netchan.message, pr_global_struct->defLosses); MSG_WriteByte (&client->netchan.message, pr_global_struct->attLosses); } } host_client->old_frags = host_client->edict->v.frags; } SV_WriteInventory(host_client, host_client->edict, &host_client->netchan.message); if (CheckPIV && host_client->PIV != host_client->LastPIV) { MSG_WriteByte (&host_client->netchan.message, svc_update_piv); MSG_WriteLong (&host_client->netchan.message, host_client->PIV); host_client->LastPIV = host_client->PIV; } // maxspeed/entgravity changes ent = host_client->edict; val = GetEdictFieldValue(ent, "gravity"); if (val && host_client->entgravity != val->_float) { host_client->entgravity = val->_float; MSG_WriteByte(&host_client->netchan.message, svc_entgravity); MSG_WriteFloat(&host_client->netchan.message, host_client->entgravity); } val = GetEdictFieldValue(ent, "maxspeed"); if (val && host_client->maxspeed != val->_float) { host_client->maxspeed = val->_float; MSG_WriteByte(&host_client->netchan.message, svc_maxspeed); MSG_WriteFloat(&host_client->netchan.message, host_client->maxspeed); } } if (sv.datagram.overflowed) SZ_Clear (&sv.datagram); // append the broadcast messages to each client messages for (j=0, client = svs.clients ; jstate < cs_connected) continue; // reliables go to all connected or spawned SZ_Write (&client->netchan.message , sv.reliable_datagram.data , sv.reliable_datagram.cursize); if (client->state != cs_spawned) continue; // datagrams only go to spawned SZ_Write (&client->datagram , sv.datagram.data , sv.datagram.cursize); } SZ_Clear (&sv.reliable_datagram); SZ_Clear (&sv.datagram); } /* ============= SV_CleanupEnts ============= */ void SV_CleanupEnts (void) { int e; edict_t *ent; ent = NEXT_EDICT(sv.edicts); for (e=1 ; ev.effects = (int)ent->v.effects & ~EF_MUZZLEFLASH; ent->v.wpn_sound = 0; } } /* ======================= SV_SendClientMessages ======================= */ void SV_SendClientMessages (void) { int i; client_t *c; // update frags, names, etc SV_UpdateToReliableMessages (); // build individual updates for (i=0, c = svs.clients ; istate) continue; // if the reliable message overflowed, // drop the client if (c->netchan.message.overflowed) { SZ_Clear (&c->netchan.message); SZ_Clear (&c->datagram); SV_BroadcastPrintf (PRINT_HIGH, "%s overflowed\n", c->name); Con_Printf ("WARNING: reliable overflow for %s\n",c->name); SV_DropClient (c); c->send_message = true; c->netchan.cleartime = 0; // don't choke this message } // only send messages if the client has sent one // and the bandwidth is not choked if (!c->send_message) continue; c->send_message = false; // try putting this after choke? if (!Netchan_CanPacket (&c->netchan)) { c->chokecount++; continue; // bandwidth choke } if (c->state == cs_spawned) SV_SendClientDatagram (c); else Netchan_Transmit (&c->netchan, 0, NULL); // just update reliable } // clear muzzle flashes & wpn_sound SV_CleanupEnts (); } /* ======================= SV_SendMessagesToAll FIXME: does this sequence right? ======================= */ void SV_SendMessagesToAll (void) { int i; client_t *c; for (i=0, c = svs.clients ; istate) // FIXME: should this only send to active? c->send_message = true; SV_SendClientMessages (); }