#include "server.h"
char sv_outputbuf[SV_OUTPUTBUF_LENGTH];
void SV_FlushRedirect(int sv_redirected, char *outputbuf) {
if(sv_redirected == RD_PACKET) {
Netchan_OutOfBandPrint(NS_SERVER, net_from, "print\n%s", outputbuf);
} else if(sv_redirected == RD_CLIENT) {
MSG_WriteByte(&sv_client->netchan.message, svc_print);
MSG_WriteByte(&sv_client->netchan.message, PRINT_HIGH);
MSG_WriteString(&sv_client->netchan.message, outputbuf);
}
}
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, sizeof(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);
}
void SV_BroadcastPrintf(int level, char *fmt, ...) {
va_list argptr;
char string[2048];
client_t *cl;
int i;
va_start(argptr, fmt);
vsprintf(string, sizeof(string), fmt, argptr);
va_end(argptr);
if(dedicated->value) {
char copy[1024];
int i;
for(i = 0; i < 1023 && string[i]; i++)
copy[i] = string[i] & 127;
copy[i] = 0;
Com_Printf("%s", copy);
}
for(i = 0, cl = svs.clients; i < maxclients->value; i++, cl++) {
if(level < cl->messagelevel)
continue;
if(cl->state != cs_spawned)
continue;
MSG_WriteByte(&cl->netchan.message, svc_print);
MSG_WriteByte(&cl->netchan.message, level);
MSG_WriteString(&cl->netchan.message, string);
}
}
void SV_BroadcastCommand(char *fmt, ...) {
va_list argptr;
char string[1024];
if(!sv.state)
return;
va_start(argptr, fmt);
vsprintf(string, sizeof(string), fmt, argptr);
va_end(argptr);
MSG_WriteByte(&sv.multicast, svc_stufftext);
MSG_WriteString(&sv.multicast, string);
SV_Multicast(CMODEL_A, NULL, MULTICAST_ALL_R);
}
void SV_Multicast(int cmodel_index, vec3_t origin, multicast_t to) {
client_t *client;
byte *mask;
int leafnum, cluster;
int j;
bool reliable;
int area1, area2;
reliable = false;
if(to != MULTICAST_ALL_R && to != MULTICAST_ALL) {
leafnum = CM_PointLeafnum(cmodel_index, origin);
area1 = CM_LeafArea(cmodel_index, leafnum);
} else {
leafnum = 0; area1 = 0;
}
if(svs.demofile)
SZ_Write(&svs.demo_multicast, sv.multicast.data, sv.multicast.cursize);
switch(to) {
case MULTICAST_ALL_R:
reliable = true; case MULTICAST_ALL:
leafnum = 0;
mask = NULL;
break;
case MULTICAST_PHS_R:
reliable = true; case MULTICAST_PHS:
leafnum = CM_PointLeafnum(cmodel_index, origin);
cluster = CM_LeafCluster(cmodel_index, leafnum);
mask = CM_ClusterPHS(cmodel_index, cluster);
break;
case MULTICAST_PVS_R:
reliable = true; case MULTICAST_PVS:
leafnum = CM_PointLeafnum(cmodel_index, origin);
cluster = CM_LeafCluster(cmodel_index, leafnum);
mask = CM_ClusterPVS(cmodel_index, cluster);
break;
default:
mask = NULL;
Com_Error(ERR_FATAL, "SV_Multicast: bad to:%i", to);
}
for(j = 0, client = svs.clients; j < maxclients->value; j++, client++) {
if(client->state == cs_free || client->state == cs_zombie)
continue;
if(client->state != cs_spawned && !reliable)
continue;
if(mask) {
leafnum = CM_PointLeafnum(cmodel_index, client->edict->s.origin);
cluster = CM_LeafCluster(cmodel_index, leafnum);
area2 = CM_LeafArea(cmodel_index, leafnum);
if(!CM_AreasConnected(cmodel_index, area1, area2))
continue;
if(mask && (!(mask[cluster >> 3] & (1 << (cluster & 7)))))
continue;
}
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);
}
void SV_StartSound(vec3_t origin, edict_t *entity, int channel, int soundindex, float volume, float attenuation,
float timeofs) {
int sendchan;
int flags;
int i;
int ent;
vec3_t origin_v;
bool use_phs;
if(volume < 0 || volume > 1.0)
Com_Error(ERR_FATAL, "SV_StartSound: volume = %f", volume);
if(attenuation < 0 || attenuation > 4)
Com_Error(ERR_FATAL, "SV_StartSound: attenuation = %f", attenuation);
if(timeofs < 0 || timeofs > 0.255)
Com_Error(ERR_FATAL, "SV_StartSound: timeofs = %f", timeofs);
ent = NUM_FOR_EDICT(entity);
if(channel & 8) {
use_phs = false;
channel &= 7;
} else
use_phs = true;
sendchan = (ent << 3) | (channel & 7);
flags = 0;
if(volume != DEFAULT_SOUND_PACKET_VOLUME)
flags |= SND_VOLUME;
if(attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
flags |= SND_ATTENUATION;
if((entity->svflags & SVF_NOCLIENT) || (entity->solid == SOLID_BSP) || origin)
flags |= SND_POS;
flags |= SND_ENT;
if(timeofs)
flags |= SND_OFFSET;
if(!origin) {
origin = origin_v;
if(entity->solid == SOLID_BSP) {
for(i = 0; i < 3; i++)
origin_v[i] = entity->s.origin[i] + 0.5 * (entity->mins[i] + entity->maxs[i]);
} else {
VectorCopy(entity->s.origin, origin_v);
}
}
int cmodel_index = entity->s.cmodel_index;
MSG_WriteByte(&sv.multicast, svc_sound);
MSG_WriteByte(&sv.multicast, flags);
MSG_WriteByte(&sv.multicast, soundindex);
if(flags & SND_VOLUME)
MSG_WriteByte(&sv.multicast, volume * 255);
if(flags & SND_ATTENUATION)
MSG_WriteByte(&sv.multicast, attenuation * 64);
if(flags & SND_OFFSET)
MSG_WriteByte(&sv.multicast, timeofs * 1000);
if(flags & SND_ENT)
MSG_WriteShort(&sv.multicast, sendchan);
if(flags & SND_POS)
MSG_WritePos(&sv.multicast, origin);
if(attenuation == ATTN_NONE)
use_phs = false;
if(channel & CHAN_RELIABLE) {
if(use_phs)
SV_Multicast(cmodel_index, origin, MULTICAST_PHS_R);
else
SV_Multicast(cmodel_index, origin, MULTICAST_ALL_R);
} else {
if(use_phs)
SV_Multicast(cmodel_index, origin, MULTICAST_PHS);
else
SV_Multicast(cmodel_index, origin, MULTICAST_ALL);
}
}
bool SV_SendClientDatagram(client_t *client) {
byte msg_buf[MAX_MSGLEN];
sizebuf_t msg;
SV_BuildClientFrame(client);
SZ_Init(&msg, msg_buf, sizeof(msg_buf));
msg.allowoverflow = true;
SV_WriteFrameToClient(client, &msg);
if(client->datagram.overflowed)
Com_Printf("WARNING: datagram overflowed for %s\n", client->name);
else
SZ_Write(&msg, client->datagram.data, client->datagram.cursize);
SZ_Clear(&client->datagram);
if(msg.overflowed) { Com_Printf("WARNING: msg overflowed for %s\n", client->name);
SZ_Clear(&msg);
}
Netchan_Transmit(&client->netchan, msg.cursize, msg.data);
client->message_size[sv.framenum % RATE_MESSAGES] = msg.cursize;
return true;
}
void SV_DemoCompleted(void) {
if(sv.demofile) {
fclose(sv.demofile);
sv.demofile = NULL;
}
SV_Nextserver();
}
bool SV_RateDrop(client_t *c) {
int total;
int i;
if(c->netchan.remote_address.type == NA_LOOPBACK)
return false;
total = 0;
for(i = 0; i < RATE_MESSAGES; i++) {
total += c->message_size[i];
}
if(total > c->rate) {
c->surpressCount++;
c->message_size[sv.framenum % RATE_MESSAGES] = 0;
return true;
}
return false;
}
void SV_SendClientMessages(void) {
int i;
client_t *c;
int msglen;
byte msgbuf[MAX_MSGLEN];
int r;
msglen = 0;
if(sv.state == ss_demo && sv.demofile) {
if(sv_paused->value)
msglen = 0;
else {
r = fread(&msglen, 4, 1, sv.demofile);
if(r != 1) {
SV_DemoCompleted();
return;
}
msglen = LittleLong(msglen);
if(msglen == -1) {
SV_DemoCompleted();
return;
}
if(msglen > MAX_MSGLEN)
Com_Error(ERR_DROP, "SV_SendClientMessages: msglen > MAX_MSGLEN");
r = fread(msgbuf, msglen, 1, sv.demofile);
if(r != 1) {
SV_DemoCompleted();
return;
}
}
}
for(i = 0, c = svs.clients; i < maxclients->value; i++, c++) {
if(!c->state)
continue;
if(c->netchan.message.overflowed) {
SZ_Clear(&c->netchan.message);
SZ_Clear(&c->datagram);
SV_BroadcastPrintf(PRINT_HIGH, "%s overflowed\n", c->name);
SV_DropClient(c);
}
if(sv.state == ss_cinematic || sv.state == ss_demo || sv.state == ss_pic)
Netchan_Transmit(&c->netchan, msglen, msgbuf);
else if(c->state == cs_spawned) {
if(SV_RateDrop(c))
continue;
SV_SendClientDatagram(c);
} else {
if(c->netchan.message.cursize || curtime - c->netchan.last_sent > 1000)
Netchan_Transmit(&c->netchan, 0, NULL);
}
}
}