/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sv_main.c -- server main program #include "q_defs.h" server_t sv; server_static_t svs; static char localmodels[MAX_MODELS][8]; // inline model names for precache int32_t sv_protocol = PROTOCOL_FITZQUAKE; //johnfitz //============================================================================ /* =============== SV_Protocol_f =============== */ void SV_Protocol_f(void) { int32_t i; switch(Cmd_Argc()) { case 1: Con_Printf("\"sv_protocol\" is \"%" PRIi32 "\"\n", sv_protocol); break; case 2: i = atoi(Cmd_Argv(1)); if(i != PROTOCOL_NETQUAKE && i != PROTOCOL_FITZQUAKE && i != PROTOCOL_RMQ) Con_Printf("sv_protocol must be %" PRIi32 " or %" PRIi32 " or %" PRIi32 "\n", PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE, PROTOCOL_RMQ); else { sv_protocol = i; if(sv.active) Con_Printf("changes will not take effect until the next level load.\n"); } break; default: Con_SafePrintf("usage: sv_protocol \n"); break; } } /* =============== SV_Init =============== */ void SV_Init(void) { int32_t i; const char *p; extern cvar_t sv_maxvelocity; extern cvar_t sv_gravity; extern cvar_t sv_nostep; extern cvar_t sv_freezenonclients; extern cvar_t sv_friction; extern cvar_t sv_edgefriction; extern cvar_t sv_stopspeed; extern cvar_t sv_maxspeed; extern cvar_t sv_accelerate; extern cvar_t sv_idealpitchscale; extern cvar_t sv_aim; extern cvar_t sv_altnoclip; //johnfitz sv.edicts = NULL; // ericw -- sv.edicts switched to use malloc() Cvar_RegisterVariable(&sv_maxvelocity); Cvar_RegisterVariable(&sv_gravity); Cvar_RegisterVariable(&sv_friction); Cvar_SetCallback(&sv_gravity, Host_Callback_Notify); Cvar_SetCallback(&sv_friction, Host_Callback_Notify); Cvar_RegisterVariable(&sv_edgefriction); Cvar_RegisterVariable(&sv_stopspeed); Cvar_RegisterVariable(&sv_maxspeed); Cvar_SetCallback(&sv_maxspeed, Host_Callback_Notify); Cvar_RegisterVariable(&sv_accelerate); Cvar_RegisterVariable(&sv_idealpitchscale); Cvar_RegisterVariable(&sv_aim); Cvar_RegisterVariable(&sv_nostep); Cvar_RegisterVariable(&sv_freezenonclients); Cvar_RegisterVariable(&sv_altnoclip); //johnfitz Cmd_AddCommand("sv_protocol", &SV_Protocol_f); //johnfitz for(i = 0 ; i < MAX_MODELS ; i++) sprintf(localmodels[i], "*%" PRIi32, i); i = COM_CheckParm("-protocol"); if(i && i < com_argc - 1) sv_protocol = atoi(com_argv[i + 1]); switch(sv_protocol) { case PROTOCOL_NETQUAKE: p = "NetQuake"; break; case PROTOCOL_FITZQUAKE: p = "FitzQuake"; break; case PROTOCOL_RMQ: p = "RMQ"; break; default: Sys_Error("Bad protocol version request %" PRIi32 ". Accepted values: %" PRIi32 ", %" PRIi32 ", %" PRIi32 ".", sv_protocol, PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE, PROTOCOL_RMQ); return; /* silence compiler */ } Sys_Printf("Server using protocol %" PRIi32 " (%s)\n", sv_protocol, p); } /* ============================================================================= EVENT MESSAGES ============================================================================= */ /* ================== SV_StartParticle Make sure the event gets sent to all clients ================== */ void SV_StartParticle(vec3_t org, vec3_t dir, int32_t color, int32_t count) { int32_t i, v; if(sv.datagram.cursize > MAX_DATAGRAM - 16) return; MSG_WriteByte(&sv.datagram, svc_particle); MSG_WriteCoord(&sv.datagram, org[0], sv.protocolflags); MSG_WriteCoord(&sv.datagram, org[1], sv.protocolflags); MSG_WriteCoord(&sv.datagram, org[2], sv.protocolflags); for(i = 0 ; i < 3 ; i++) { v = dir[i] * 16; if(v > 127) v = 127; else if(v < -128) v = -128; MSG_WriteChar(&sv.datagram, v); } MSG_WriteByte(&sv.datagram, count); MSG_WriteByte(&sv.datagram, color); } /* ================== 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, int32_t channel, const char *sample, int32_t volume, float attenuation) { int32_t sound_num, ent; int32_t i, field_mask; if(volume < 0 || volume > 255) Host_Error("SV_StartSound: volume = %" PRIi32, volume); if(attenuation < 0 || attenuation > 4) Host_Error("SV_StartSound: attenuation = %f", attenuation); if(channel < 0 || channel > 7) Host_Error("SV_StartSound: channel = %" PRIi32, channel); if(sv.datagram.cursize > MAX_DATAGRAM - 16) return; // find precache number for sound for(sound_num = 1; sound_num < MAX_SOUNDS && sv.sound_precache[sound_num]; sound_num++) { if(!strcmp(sample, sv.sound_precache[sound_num])) break; } if(sound_num == MAX_SOUNDS || !sv.sound_precache[sound_num]) { Con_Printf("SV_StartSound: %s not precacheed\n", sample); return; } ent = NumForEdict(entity); field_mask = 0; if(volume != DEFAULT_SOUND_PACKET_VOLUME) field_mask |= SND_VOLUME; if(attenuation != DEFAULT_SOUND_PACKET_ATTENUATION) field_mask |= SND_ATTENUATION; //johnfitz -- PROTOCOL_FITZQUAKE if(ent >= 8192) { if(sv.protocol == PROTOCOL_NETQUAKE) return; //don't send any info protocol can't support else field_mask |= SND_LARGEENTITY; } if(sound_num >= 256 || channel >= 8) { if(sv.protocol == PROTOCOL_NETQUAKE) return; //don't send any info protocol can't support else field_mask |= SND_LARGESOUND; } //johnfitz // directed messages go only to the entity the are targeted on MSG_WriteByte(&sv.datagram, svc_sound); MSG_WriteByte(&sv.datagram, field_mask); if(field_mask & SND_VOLUME) MSG_WriteByte(&sv.datagram, volume); if(field_mask & SND_ATTENUATION) MSG_WriteByte(&sv.datagram, attenuation * 64); //johnfitz -- PROTOCOL_FITZQUAKE if(field_mask & SND_LARGEENTITY) { MSG_WriteShort(&sv.datagram, ent); MSG_WriteByte(&sv.datagram, channel); } else MSG_WriteShort(&sv.datagram, (ent << 3) | channel); if(field_mask & SND_LARGESOUND) MSG_WriteShort(&sv.datagram, sound_num); else MSG_WriteByte(&sv.datagram, sound_num); //johnfitz for(i = 0; i < 3; i++) MSG_WriteCoord(&sv.datagram, ED_Vector(entity, ED_origin)[i] + 0.5 * (ED_Vector(entity, ED_mins)[i] + ED_Vector(entity, ED_maxs)[i]), sv.protocolflags); } /* ============================================================================== CLIENT SPAWNING ============================================================================== */ /* ================ SV_SendServerinfo Sends the first message from the server to a connected client. This will be sent on the initial connection and upon each server load. ================ */ void SV_SendServerinfo(client_t *client) { const char **s; char message[2048]; int32_t i; //johnfitz MSG_WriteByte(&client->message, svc_print); sprintf(message, "\x02\nFITZQUAKE 0.85 SERVER (%" PRIi32 " CRC)\n", pr_crc); MSG_WriteString(&client->message, message); MSG_WriteByte(&client->message, svc_serverinfo); MSG_WriteLong(&client->message, sv.protocol); //johnfitz -- sv.protocol instead of PROTOCOL_VERSION if(sv.protocol == PROTOCOL_RMQ) { // mh - now send protocol flags so that the client knows the protocol features to expect MSG_WriteLong(&client->message, sv.protocolflags); } MSG_WriteByte(&client->message, svs.maxclients); if(!coop.value && deathmatch.value) MSG_WriteByte(&client->message, GAME_DEATHMATCH); else MSG_WriteByte(&client->message, GAME_COOP); MSG_WriteString(&client->message, ED_String(sv.edicts, ED_message)); //johnfitz -- only send the first 256 model and sound precaches if protocol is 15 for(i = 0, s = sv.model_precache + 1 ; *s; s++, i++) if(sv.protocol != PROTOCOL_NETQUAKE || i < 256) MSG_WriteString(&client->message, *s); MSG_WriteByte(&client->message, 0); for(i = 0, s = sv.sound_precache + 1 ; *s ; s++, i++) if(sv.protocol != PROTOCOL_NETQUAKE || i < 256) MSG_WriteString(&client->message, *s); MSG_WriteByte(&client->message, 0); //johnfitz // send music MSG_WriteByte(&client->message, svc_cdtrack); MSG_WriteByte(&client->message, ED_Float(sv.edicts, ED_sounds)); MSG_WriteByte(&client->message, ED_Float(sv.edicts, ED_sounds)); // set view MSG_WriteByte(&client->message, svc_setview); MSG_WriteShort(&client->message, NumForEdict(client->edict)); MSG_WriteByte(&client->message, svc_signonnum); MSG_WriteByte(&client->message, 1); client->sendsignon = true; client->spawned = false; // need prespawn, spawn, etc } /* ================ SV_ConnectClient Initializes a client_t for a new net connection. This will only be called once for a player each game, not once for each level change. ================ */ void SV_ConnectClient(int32_t clientnum) { edict_t *ent; client_t *client; int32_t edictnum; struct qsocket_s *netconnection; int32_t i; float spawn_parms[NUM_SPAWN_PARMS]; client = svs.clients + clientnum; Con_DPrintf("Client %s connected\n", NET_QSocketGetAddressString(client->netconnection)); edictnum = clientnum + 1; ent = EdictNum(edictnum); // set up the client_t netconnection = client->netconnection; if(sv.loadgame) memcpy(spawn_parms, client->spawn_parms, sizeof(spawn_parms)); memset(client, 0, sizeof(*client)); client->netconnection = netconnection; strcpy(client->name, "unconnected"); client->active = true; client->spawned = false; client->edict = ent; client->message.data = client->msgbuf; client->message.maxsize = sizeof(client->msgbuf); client->message.allowoverflow = true; // we can catch it if(sv.loadgame) memcpy(client->spawn_parms, spawn_parms, sizeof(spawn_parms)); else { // call the progs to get default spawn parms for the new client PR_ExecuteProgram(G_Func(GBL_SetNewParms)); for(i = 0 ; i < NUM_SPAWN_PARMS ; i++) client->spawn_parms[i] = (&G_Float(GBL_parm1))[i]; } SV_SendServerinfo(client); } /* =================== SV_CheckForNewClients =================== */ void SV_CheckForNewClients(void) { struct qsocket_s *ret; int32_t i; // // check for new connections // while(1) { ret = NET_CheckNewConnections(); if(!ret) break; // // init a new client structure // for(i = 0 ; i < svs.maxclients ; i++) if(!svs.clients[i].active) break; if(i == svs.maxclients) Sys_Error("Host_CheckForNewClients: no free clients"); svs.clients[i].netconnection = ret; SV_ConnectClient(i); net_activeconnections++; } } /* =============================================================================== FRAME UPDATES =============================================================================== */ /* ================== SV_ClearDatagram ================== */ void SV_ClearDatagram(void) { SZ_Clear(&sv.datagram); } /* ============================================================================= The PVS must include a small area around the client to allow head bobbing or other small motion on the client side. Otherwise, a bob might cause an entity that should be visible to not show up, especially when the bob crosses a waterline. ============================================================================= */ static int32_t fatbytes; static byte *fatpvs; static int32_t fatpvs_capacity; void SV_AddToFatPVS(vec3_t org, mnode_t *node, qmodel_t *worldmodel) //johnfitz -- added worldmodel as a parameter { int32_t i; byte *pvs; mplane_t *plane; float d; while(1) { // if this is a leaf, accumulate the pvs bits if(node->contents < 0) { if(node->contents != CONTENTS_SOLID) { pvs = Mod_LeafPVS((mleaf_t *)node, worldmodel); //johnfitz -- worldmodel as a parameter for(i = 0 ; i < fatbytes ; i++) fatpvs[i] |= pvs[i]; } return; } plane = node->plane; d = DotProduct(org, plane->normal) - plane->dist; if(d > 8) node = node->children[0]; else if(d < -8) node = node->children[1]; else { // go down both SV_AddToFatPVS(org, node->children[0], worldmodel); //johnfitz -- worldmodel as a parameter node = node->children[1]; } } } /* ============= SV_FatPVS Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the given point. ============= */ byte *SV_FatPVS(vec3_t org, qmodel_t *worldmodel) //johnfitz -- added worldmodel as a parameter { fatbytes = (worldmodel->numleafs + 7) >> 3; // ericw -- was +31, assumed to be a bug/typo if(fatpvs == NULL || fatbytes > fatpvs_capacity) { fatpvs_capacity = fatbytes; fatpvs = (byte *) realloc(fatpvs, fatpvs_capacity); if(!fatpvs) Sys_Error("SV_FatPVS: realloc() failed on %" PRIi32 " bytes", fatpvs_capacity); } memset(fatpvs, 0, fatbytes); SV_AddToFatPVS(org, worldmodel->nodes, worldmodel); //johnfitz -- worldmodel as a parameter return fatpvs; } /* ============= SV_VisibleToClient -- johnfitz PVS test encapsulated in a nice function ============= */ bool SV_VisibleToClient(edict_t *client, edict_t *test, qmodel_t *worldmodel) { byte *pvs; vec3_t org; int32_t i; VectorAdd(ED_Vector(client, ED_origin), ED_Vector(client, ED_view_ofs), org); pvs = SV_FatPVS(org, worldmodel); for(i = 0 ; i < test->num_leafs ; i++) if(pvs[test->leafnums[i] >> 3] & (1 << (test->leafnums[i] & 7))) return true; return false; } //============================================================================= /* ============= SV_WriteEntitiesToClient ============= */ void SV_WriteEntitiesToClient(edict_t *clent, sizebuf_t *msg) { int32_t e, i; int32_t bits; byte *pvs; vec3_t org; float miss; edict_t *ent; // find the client's PVS VectorAdd(ED_Vector(clent, ED_origin), ED_Vector(clent, ED_view_ofs), org); pvs = SV_FatPVS(org, sv.worldmodel); // send over all entities (excpet the client) that touch the pvs ent = NextEdict(sv.edicts); for(e = 1 ; e < sv.num_edicts ; e++, ent = NextEdict(ent)) { if(ent != clent) // clent is ALLWAYS sent { // ignore ents without visible models if(!ED_Float(ent, ED_modelindex) || !ED_String(ent, ED_model)[0]) continue; //johnfitz -- don't send model>255 entities if protocol is 15 if(sv.protocol == PROTOCOL_NETQUAKE && (int32_t)ED_Float(ent, ED_modelindex) & 0xFF00) continue; // ignore if not touching a PV leaf for(i = 0 ; i < ent->num_leafs ; i++) if(pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i] & 7))) break; // ericw -- added ent->num_leafs < MAX_ENT_LEAFS condition. // // if ent->num_leafs == MAX_ENT_LEAFS, the ent is visible from too many leafs // for us to say whether it's in the PVS, so don't try to vis cull it. // this commonly happens with rotators, because they often have huge bboxes // spanning the entire map, or really tall lifts, etc. if(i == ent->num_leafs && ent->num_leafs < MAX_ENT_LEAFS) continue; // not visible } //johnfitz -- max size for protocol 15 is 18 bytes, not 16 as originally //assumed here. And, for protocol 85 the max size is actually 24 bytes. if(msg->cursize + 24 > msg->maxsize) { //johnfitz -- less spammy overflow message if(!dev_overflows.packetsize || dev_overflows.packetsize + CONSOLE_RESPAM_TIME < realtime) { Con_Printf("Packet overflow!\n"); dev_overflows.packetsize = realtime; } goto stats; //johnfitz } // send an update bits = 0; for(i = 0 ; i < 3 ; i++) { miss = ED_Vector(ent, ED_origin)[i] - ent->baseline.origin[i]; if(miss < -0.1 || miss > 0.1) bits |= U_ORIGIN1 << i; } if(ED_Vector(ent, ED_angles)[0] != ent->baseline.angles[0]) bits |= U_ANGLE1; if(ED_Vector(ent, ED_angles)[1] != ent->baseline.angles[1]) bits |= U_ANGLE2; if(ED_Vector(ent, ED_angles)[2] != ent->baseline.angles[2]) bits |= U_ANGLE3; if(ED_Float(ent, ED_movetype) == MOVETYPE_STEP) bits |= U_STEP; // don't mess up the step animation if(ent->baseline.colormap != ED_Float(ent, ED_colormap)) bits |= U_COLORMAP; if(ent->baseline.skin != ED_Float(ent, ED_skin)) bits |= U_SKIN; if(ent->baseline.frame != ED_Float(ent, ED_frame)) bits |= U_FRAME; if(ent->baseline.effects != ED_Float(ent, ED_effects)) bits |= U_EFFECTS; if(ent->baseline.modelindex != ED_Float(ent, ED_modelindex)) bits |= U_MODEL; //johnfitz -- alpha if(pr_alpha_supported) { // TODO: find a cleaner place to put this code eval_t *val; val = GetEdictFieldValue(ent, "alpha"); if(val) ent->alpha = ENTALPHA_ENCODE(val->flt); } //don't send invisible entities unless they have effects if(ent->alpha == ENTALPHA_ZERO && !ED_Float(ent, ED_effects)) continue; //johnfitz //johnfitz -- PROTOCOL_FITZQUAKE if(sv.protocol != PROTOCOL_NETQUAKE) { if(ent->baseline.alpha != ent->alpha) bits |= U_ALPHA; if(bits & U_FRAME && (int32_t)ED_Float(ent, ED_frame) & 0xFF00) bits |= U_FRAME2; if(bits & U_MODEL && (int32_t)ED_Float(ent, ED_modelindex) & 0xFF00) bits |= U_MODEL2; if(ent->sendinterval) bits |= U_LERPFINISH; if(bits >= 65536) bits |= U_EXTEND1; if(bits >= 16777216) bits |= U_EXTEND2; } //johnfitz if(e >= 256) bits |= U_LONGENTITY; if(bits >= 256) bits |= U_MOREBITS; // // write the message // MSG_WriteByte(msg, bits | U_SIGNAL); if(bits & U_MOREBITS) MSG_WriteByte(msg, bits >> 8); //johnfitz -- PROTOCOL_FITZQUAKE if(bits & U_EXTEND1) MSG_WriteByte(msg, bits >> 16); if(bits & U_EXTEND2) MSG_WriteByte(msg, bits >> 24); //johnfitz if(bits & U_LONGENTITY) MSG_WriteShort(msg, e); else MSG_WriteByte(msg, e); if(bits & U_MODEL) MSG_WriteByte(msg, ED_Float(ent, ED_modelindex)); if(bits & U_FRAME) MSG_WriteByte(msg, ED_Float(ent, ED_frame)); if(bits & U_COLORMAP) MSG_WriteByte(msg, ED_Float(ent, ED_colormap)); if(bits & U_SKIN) MSG_WriteByte(msg, ED_Float(ent, ED_skin)); if(bits & U_EFFECTS) MSG_WriteByte(msg, ED_Float(ent, ED_effects)); if(bits & U_ORIGIN1) MSG_WriteCoord(msg, ED_Vector(ent, ED_origin)[0], sv.protocolflags); if(bits & U_ANGLE1) MSG_WriteAngle(msg, ED_Vector(ent, ED_angles)[0], sv.protocolflags); if(bits & U_ORIGIN2) MSG_WriteCoord(msg, ED_Vector(ent, ED_origin)[1], sv.protocolflags); if(bits & U_ANGLE2) MSG_WriteAngle(msg, ED_Vector(ent, ED_angles)[1], sv.protocolflags); if(bits & U_ORIGIN3) MSG_WriteCoord(msg, ED_Vector(ent, ED_origin)[2], sv.protocolflags); if(bits & U_ANGLE3) MSG_WriteAngle(msg, ED_Vector(ent, ED_angles)[2], sv.protocolflags); //johnfitz -- PROTOCOL_FITZQUAKE if(bits & U_ALPHA) MSG_WriteByte(msg, ent->alpha); if(bits & U_FRAME2) MSG_WriteByte(msg, (int32_t)ED_Float(ent, ED_frame) >> 8); if(bits & U_MODEL2) MSG_WriteByte(msg, (int32_t)ED_Float(ent, ED_modelindex) >> 8); if(bits & U_LERPFINISH) MSG_WriteByte(msg, (byte)(Q_rint((ED_Float(ent, ED_nextthink) - sv.time) * 255))); //johnfitz } //johnfitz -- devstats stats: if(msg->cursize > 1024 && dev_peakstats.packetsize <= 1024) Con_DWarning("%" PRIi32 " byte packet exceeds standard limit of 1024 (max = %" PRIi32 ").\n", msg->cursize, msg->maxsize); dev_stats.packetsize = msg->cursize; dev_peakstats.packetsize = q_max(msg->cursize, dev_peakstats.packetsize); //johnfitz } /* ============= SV_CleanupEnts ============= */ void SV_CleanupEnts(void) { int32_t e; edict_t *ent; ent = NextEdict(sv.edicts); for(e = 1 ; e < sv.num_edicts ; e++, ent = NextEdict(ent)) { ED_Float(ent, ED_effects) = (int32_t)ED_Float(ent, ED_effects) & ~EF_MUZZLEFLASH; } } /* ================== SV_WriteClientdataToMessage ================== */ void SV_WriteClientdataToMessage(edict_t *ent, sizebuf_t *msg) { int32_t bits; int32_t i; edict_t *other; int32_t items; eval_t *val; // // send a damage message // if(ED_Float(ent, ED_dmg_take) || ED_Float(ent, ED_dmg_save)) { other = ProgEdict(ED_PEdict(ent, ED_dmg_inflictor)); MSG_WriteByte(msg, svc_damage); MSG_WriteByte(msg, ED_Float(ent, ED_dmg_save)); MSG_WriteByte(msg, ED_Float(ent, ED_dmg_take)); for(i = 0 ; i < 3 ; i++) MSG_WriteCoord(msg, ED_Vector(other, ED_origin)[i] + 0.5 * (ED_Vector(other, ED_mins)[i] + ED_Vector(other, ED_maxs)[i]), sv.protocolflags); ED_Float(ent, ED_dmg_take) = 0; ED_Float(ent, ED_dmg_save) = 0; } // // send the current viewpos offset from the view entity // SV_SetIdealPitch(); // how much to look up / down ideally // a fixangle might get lost in a dropped packet. Oh well. if(ED_Float(ent, ED_fixangle)) { MSG_WriteByte(msg, svc_setangle); for(i = 0 ; i < 3 ; i++) MSG_WriteAngle(msg, ED_Vector(ent, ED_angles)[i], sv.protocolflags); ED_Float(ent, ED_fixangle) = 0; } bits = 0; if(ED_Vector(ent, ED_view_ofs)[2] != DEFAULT_VIEWHEIGHT) bits |= SU_VIEWHEIGHT; if(ED_Float(ent, ED_idealpitch)) bits |= SU_IDEALPITCH; // stuff the sigil bits into the high bits of items for sbar, or else // mix in items2 val = GetEdictFieldValue(ent, "items2"); if(val) items = (int32_t)ED_Float(ent, ED_items) | ((int32_t)val->flt << 23); else items = (int32_t)ED_Float(ent, ED_items) | ((int32_t)G_Float(GBL_serverflags) << 28); bits |= SU_ITEMS; if((int32_t)ED_Float(ent, ED_flags) & FL_ONGROUND) bits |= SU_ONGROUND; if(ED_Float(ent, ED_waterlevel) >= 2) bits |= SU_INWATER; for(i = 0 ; i < 3 ; i++) { if(ED_Vector(ent, ED_punchangle)[i]) bits |= (SU_PUNCH1 << i); if(ED_Vector(ent, ED_velocity)[i]) bits |= (SU_VELOCITY1 << i); } if(ED_Float(ent, ED_weaponframe)) bits |= SU_WEAPONFRAME; if(ED_Float(ent, ED_armorvalue)) bits |= SU_ARMOR; // if (ED_Float(ent, ED_weapon)) bits |= SU_WEAPON; //johnfitz -- PROTOCOL_FITZQUAKE if(sv.protocol != PROTOCOL_NETQUAKE) { if(bits & SU_WEAPON && SV_ModelIndex(ED_String(ent, ED_weaponmodel)) & 0xFF00) bits |= SU_WEAPON2; if((int32_t)ED_Float(ent, ED_armorvalue) & 0xFF00) bits |= SU_ARMOR2; if((int32_t)ED_Float(ent, ED_currentammo) & 0xFF00) bits |= SU_AMMO2; if((int32_t)ED_Float(ent, ED_ammo_shells) & 0xFF00) bits |= SU_SHELLS2; if((int32_t)ED_Float(ent, ED_ammo_nails) & 0xFF00) bits |= SU_NAILS2; if((int32_t)ED_Float(ent, ED_ammo_rockets) & 0xFF00) bits |= SU_ROCKETS2; if((int32_t)ED_Float(ent, ED_ammo_cells) & 0xFF00) bits |= SU_CELLS2; if(bits & SU_WEAPONFRAME && (int32_t)ED_Float(ent, ED_weaponframe) & 0xFF00) bits |= SU_WEAPONFRAME2; if(bits & SU_WEAPON && ent->alpha != ENTALPHA_DEFAULT) bits |= SU_WEAPONALPHA; //for now, weaponalpha = client entity alpha if(bits >= 65536) bits |= SU_EXTEND1; if(bits >= 16777216) bits |= SU_EXTEND2; } //johnfitz // send the data MSG_WriteByte(msg, svc_clientdata); MSG_WriteShort(msg, bits); //johnfitz -- PROTOCOL_FITZQUAKE if(bits & SU_EXTEND1) MSG_WriteByte(msg, bits >> 16); if(bits & SU_EXTEND2) MSG_WriteByte(msg, bits >> 24); //johnfitz if(bits & SU_VIEWHEIGHT) MSG_WriteChar(msg, ED_Vector(ent, ED_view_ofs)[2]); if(bits & SU_IDEALPITCH) MSG_WriteChar(msg, ED_Float(ent, ED_idealpitch)); for(i = 0 ; i < 3 ; i++) { if(bits & (SU_PUNCH1 << i)) MSG_WriteChar(msg, ED_Vector(ent, ED_punchangle)[i]); if(bits & (SU_VELOCITY1 << i)) MSG_WriteChar(msg, ED_Vector(ent, ED_velocity)[i] / 16); } // [always sent] if (bits & SU_ITEMS) MSG_WriteLong(msg, items); if(bits & SU_WEAPONFRAME) MSG_WriteByte(msg, ED_Float(ent, ED_weaponframe)); if(bits & SU_ARMOR) MSG_WriteByte(msg, ED_Float(ent, ED_armorvalue)); if(bits & SU_WEAPON) MSG_WriteByte(msg, SV_ModelIndex(ED_String(ent, ED_weaponmodel))); MSG_WriteShort(msg, ED_Float(ent, ED_health)); MSG_WriteByte(msg, ED_Float(ent, ED_currentammo)); MSG_WriteByte(msg, ED_Float(ent, ED_ammo_shells)); MSG_WriteByte(msg, ED_Float(ent, ED_ammo_nails)); MSG_WriteByte(msg, ED_Float(ent, ED_ammo_rockets)); MSG_WriteByte(msg, ED_Float(ent, ED_ammo_cells)); if(standard_quake) { MSG_WriteByte(msg, ED_Float(ent, ED_weapon)); } else { for(i = 0; i < 32; i++) { if(((int32_t)ED_Float(ent, ED_weapon)) & (1 << i)) { MSG_WriteByte(msg, i); break; } } } //johnfitz -- PROTOCOL_FITZQUAKE if(bits & SU_WEAPON2) MSG_WriteByte(msg, SV_ModelIndex(ED_String(ent, ED_weaponmodel)) >> 8); if(bits & SU_ARMOR2) MSG_WriteByte(msg, (int32_t)ED_Float(ent, ED_armorvalue) >> 8); if(bits & SU_AMMO2) MSG_WriteByte(msg, (int32_t)ED_Float(ent, ED_currentammo) >> 8); if(bits & SU_SHELLS2) MSG_WriteByte(msg, (int32_t)ED_Float(ent, ED_ammo_shells) >> 8); if(bits & SU_NAILS2) MSG_WriteByte(msg, (int32_t)ED_Float(ent, ED_ammo_nails) >> 8); if(bits & SU_ROCKETS2) MSG_WriteByte(msg, (int32_t)ED_Float(ent, ED_ammo_rockets) >> 8); if(bits & SU_CELLS2) MSG_WriteByte(msg, (int32_t)ED_Float(ent, ED_ammo_cells) >> 8); if(bits & SU_WEAPONFRAME2) MSG_WriteByte(msg, (int32_t)ED_Float(ent, ED_weaponframe) >> 8); if(bits & SU_WEAPONALPHA) MSG_WriteByte(msg, ent->alpha); //for now, weaponalpha = client entity alpha //johnfitz } /* ======================= SV_SendClientDatagram ======================= */ bool SV_SendClientDatagram(client_t *client) { byte buf[MAX_DATAGRAM]; sizebuf_t msg; msg.data = buf; msg.maxsize = sizeof(buf); msg.cursize = 0; //johnfitz -- if client is nonlocal, use smaller max size so packets aren't fragmented if(strcmp(NET_QSocketGetAddressString(client->netconnection), "LOCAL") != 0) msg.maxsize = DATAGRAM_MTU; //johnfitz MSG_WriteByte(&msg, svc_time); MSG_WriteFloat(&msg, sv.time); // add the client specific data to the datagram SV_WriteClientdataToMessage(client->edict, &msg); SV_WriteEntitiesToClient(client->edict, &msg); // copy the server datagram if there is space if(msg.cursize + sv.datagram.cursize < msg.maxsize) SZ_Write(&msg, sv.datagram.data, sv.datagram.cursize); // send the datagram if(NET_SendUnreliableMessage(client->netconnection, &msg) == -1) { SV_DropClient(true); // if the message couldn't send, kick off return false; } return true; } /* ======================= SV_UpdateToReliableMessages ======================= */ void SV_UpdateToReliableMessages(void) { int32_t i, j; client_t *client; // check for changes to be sent over the reliable streams for(i = 0, host_client = svs.clients ; i < svs.maxclients ; i++, host_client++) { if(host_client->old_frags != ED_Float(host_client->edict, ED_frags)) { for(j = 0, client = svs.clients ; j < svs.maxclients ; j++, client++) { if(!client->active) continue; MSG_WriteByte(&client->message, svc_updatefrags); MSG_WriteByte(&client->message, i); MSG_WriteShort(&client->message, ED_Float(host_client->edict, ED_frags)); } host_client->old_frags = ED_Float(host_client->edict, ED_frags); } } for(j = 0, client = svs.clients ; j < svs.maxclients ; j++, client++) { if(!client->active) continue; SZ_Write(&client->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize); } SZ_Clear(&sv.reliable_datagram); } /* ======================= SV_SendNop Send a nop message without trashing or sending the accumulated client message buffer ======================= */ void SV_SendNop(client_t *client) { sizebuf_t msg; byte buf[4]; msg.data = buf; msg.maxsize = sizeof(buf); msg.cursize = 0; MSG_WriteChar(&msg, svc_nop); if(NET_SendUnreliableMessage(client->netconnection, &msg) == -1) SV_DropClient(true); // if the message couldn't send, kick off client->last_message = realtime; } /* ======================= SV_SendClientMessages ======================= */ void SV_SendClientMessages(void) { int32_t i; // update frags, names, etc SV_UpdateToReliableMessages(); // build individual updates for(i = 0, host_client = svs.clients ; i < svs.maxclients ; i++, host_client++) { if(!host_client->active) continue; if(host_client->spawned) { if(!SV_SendClientDatagram(host_client)) continue; } else { // the player isn't totally in the game yet // send small keepalive messages if too much time has passed // send a full message when the next signon stage has been requested // some other message data (name changes, etc) may accumulate // between signon stages if(!host_client->sendsignon) { if(realtime - host_client->last_message > 5) SV_SendNop(host_client); continue; // don't send out non-signon messages } } // 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); host_client->message.overflowed = false; continue; } if(host_client->message.cursize || host_client->dropasap) { if(!NET_CanSendMessage(host_client->netconnection)) { // I_Printf ("can't write\n"); continue; } if(host_client->dropasap) SV_DropClient(false); // went to another level else { if(NET_SendMessage(host_client->netconnection , &host_client->message) == -1) SV_DropClient(true); // if the message couldn't send, kick off SZ_Clear(&host_client->message); host_client->last_message = realtime; host_client->sendsignon = false; } } } // clear muzzle flashes SV_CleanupEnts(); } /* ============================================================================== SERVER SPAWNING ============================================================================== */ /* ================ SV_ModelIndex ================ */ int32_t SV_ModelIndex(const char *name) { int32_t i; if(!name || !name[0]) return 0; for(i = 0 ; i < MAX_MODELS && sv.model_precache[i] ; i++) if(!strcmp(sv.model_precache[i], name)) return i; if(i == MAX_MODELS || !sv.model_precache[i]) Sys_Error("SV_ModelIndex: model %s not precached", name); return i; } /* ================ SV_CreateBaseline ================ */ void SV_CreateBaseline(void) { int32_t i; edict_t *svent; int32_t entnum; int32_t bits; //johnfitz -- PROTOCOL_FITZQUAKE for(entnum = 0; entnum < sv.num_edicts ; entnum++) { // get the current server version svent = EdictNum(entnum); if(svent->free) continue; if(entnum > svs.maxclients && !ED_Float(svent, ED_modelindex)) continue; // // create entity baseline // VectorCopy(ED_Vector(svent, ED_origin), svent->baseline.origin); VectorCopy(ED_Vector(svent, ED_angles), svent->baseline.angles); svent->baseline.frame = ED_Float(svent, ED_frame); svent->baseline.skin = ED_Float(svent, ED_skin); if(entnum > 0 && entnum <= svs.maxclients) { svent->baseline.colormap = entnum; svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl"); svent->baseline.alpha = ENTALPHA_DEFAULT; //johnfitz -- alpha support } else { svent->baseline.colormap = 0; svent->baseline.modelindex = SV_ModelIndex(ED_String(svent, ED_model)); svent->baseline.alpha = svent->alpha; //johnfitz -- alpha support } //johnfitz -- PROTOCOL_FITZQUAKE bits = 0; if(sv.protocol == PROTOCOL_NETQUAKE) //still want to send baseline in PROTOCOL_NETQUAKE, so reset these values { if(svent->baseline.modelindex & 0xFF00) svent->baseline.modelindex = 0; if(svent->baseline.frame & 0xFF00) svent->baseline.frame = 0; svent->baseline.alpha = ENTALPHA_DEFAULT; } else //decide which extra data needs to be sent { if(svent->baseline.modelindex & 0xFF00) bits |= B_LARGEMODEL; if(svent->baseline.frame & 0xFF00) bits |= B_LARGEFRAME; if(svent->baseline.alpha != ENTALPHA_DEFAULT) bits |= B_ALPHA; } //johnfitz // // add to the message // //johnfitz -- PROTOCOL_FITZQUAKE if(bits) MSG_WriteByte(&sv.signon, svc_spawnbaseline2); else MSG_WriteByte(&sv.signon, svc_spawnbaseline); //johnfitz MSG_WriteShort(&sv.signon, entnum); //johnfitz -- PROTOCOL_FITZQUAKE if(bits) MSG_WriteByte(&sv.signon, bits); if(bits & B_LARGEMODEL) MSG_WriteShort(&sv.signon, svent->baseline.modelindex); else MSG_WriteByte(&sv.signon, svent->baseline.modelindex); if(bits & B_LARGEFRAME) MSG_WriteShort(&sv.signon, svent->baseline.frame); else MSG_WriteByte(&sv.signon, svent->baseline.frame); //johnfitz MSG_WriteByte(&sv.signon, svent->baseline.colormap); MSG_WriteByte(&sv.signon, svent->baseline.skin); for(i = 0 ; i < 3 ; i++) { MSG_WriteCoord(&sv.signon, svent->baseline.origin[i], sv.protocolflags); MSG_WriteAngle(&sv.signon, svent->baseline.angles[i], sv.protocolflags); } //johnfitz -- PROTOCOL_FITZQUAKE if(bits & B_ALPHA) MSG_WriteByte(&sv.signon, svent->baseline.alpha); //johnfitz } } /* ================ SV_SendReconnect Tell all the clients that the server is changing levels ================ */ void SV_SendReconnect(void) { byte data[128]; sizebuf_t msg; msg.data = data; msg.cursize = 0; msg.maxsize = sizeof(data); MSG_WriteChar(&msg, svc_stufftext); MSG_WriteString(&msg, "reconnect\n"); NET_SendToAll(&msg, 5.0); if(!isDedicated) Cmd_ExecuteString("reconnect\n", src_command); } /* ================ SV_SaveSpawnparms Grabs the current state of each client for saving across the transition to another level ================ */ void SV_SaveSpawnparms(void) { int32_t i, j; svs.serverflags = G_Float(GBL_serverflags); for(i = 0, host_client = svs.clients ; i < svs.maxclients ; i++, host_client++) { if(!host_client->active) continue; // call the progs to get default spawn parms for the new client G_PEdict(GBL_self) = EdictProg(host_client->edict); PR_ExecuteProgram(G_Func(GBL_SetChangeParms)); for(j = 0 ; j < NUM_SPAWN_PARMS ; j++) host_client->spawn_parms[j] = (&G_Float(GBL_parm1))[j]; } } /* ================ SV_SpawnServer This is called at the start of each level ================ */ extern float scr_centertime_off; void SV_SpawnServer(const char *server) { static char dummy[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; edict_t *ent; int32_t i; // let's not have any servers with no name if(hostname.string[0] == 0) Cvar_Set("hostname", "UNNAMED"); scr_centertime_off = 0; Con_DPrintf("SpawnServer: %s\n", server); svs.changelevel_issued = false; // now safe to issue another // // tell all connected clients that we are going to a new level // if(sv.active) { SV_SendReconnect(); } // // make cvars consistant // if(coop.value) Cvar_Set("deathmatch", "0"); current_skill = (int32_t)(skill.value + 0.5); if(current_skill < 0) current_skill = 0; if(current_skill > 3) current_skill = 3; Cvar_SetValue("skill", (float)current_skill); // // set up the new server // //memset (&sv, 0, sizeof(sv)); Host_ClearMemory(); q_strlcpy(sv.name, server, sizeof(sv.name)); sv.protocol = sv_protocol; // johnfitz if(sv.protocol == PROTOCOL_RMQ) { // set up the protocol flags used by this server // (note - these could be cvar-ised so that server admins could choose the protocol features used by their servers) sv.protocolflags = PRFL_INT32COORD | PRFL_SHORTANGLE; } else sv.protocolflags = 0; // load progs to get entity field count PR_LoadProgs(); // allocate server memory /* Host_ClearMemory() called above already cleared the whole sv structure */ sv.max_edicts = CLAMP(MIN_EDICTS, (int32_t)max_edicts.value, MAX_EDICTS); //johnfitz -- max_edicts cvar sv.edicts = (edict_t *) malloc(sv.max_edicts * pr_edict_size); // ericw -- sv.edicts switched to use malloc() sv.datagram.maxsize = sizeof(sv.datagram_buf); sv.datagram.cursize = 0; sv.datagram.data = sv.datagram_buf; sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf); sv.reliable_datagram.cursize = 0; sv.reliable_datagram.data = sv.reliable_datagram_buf; sv.signon.maxsize = sizeof(sv.signon_buf); sv.signon.cursize = 0; sv.signon.data = sv.signon_buf; // leave slots at start for clients only sv.num_edicts = svs.maxclients + 1; memset(sv.edicts, 0, sv.num_edicts * pr_edict_size); // ericw -- sv.edicts switched to use malloc() for(i = 0 ; i < svs.maxclients ; i++) { ent = EdictNum(i + 1); svs.clients[i].edict = ent; } sv.state = ss_loading; sv.paused = false; sv.time = 1.0; q_strlcpy(sv.name, server, sizeof(sv.name)); q_snprintf(sv.modelname, sizeof(sv.modelname), "maps/%s.bsp", server); sv.worldmodel = Mod_ForName(sv.modelname, false); if(!sv.worldmodel) { Con_Printf("Couldn't spawn server %s\n", sv.modelname); sv.active = false; return; } sv.models[1] = sv.worldmodel; // // clear world interaction links // SV_ClearWorld(); sv.sound_precache[0] = dummy; sv.model_precache[0] = dummy; sv.model_precache[1] = sv.modelname; for(i = 1 ; i < sv.worldmodel->numsubmodels ; i++) { sv.model_precache[1 + i] = localmodels[i]; sv.models[i + 1] = Mod_ForName(localmodels[i], false); } // // load the rest of the entities // ent = EdictNum(0); memset(ent->fields, 0, progs.entityfields * 4); ent->free = false; ED_RString(ent, ED_model) = PR_SetEngineString(sv.worldmodel->name); ED_Float(ent, ED_modelindex) = 1; // world model ED_Float(ent, ED_solid) = SOLID_BSP; ED_Float(ent, ED_movetype) = MOVETYPE_PUSH; if(coop.value) G_Float(GBL_coop) = coop.value; else G_Float(GBL_deathmatch) = deathmatch.value; G_RString(GBL_mapname) = PR_SetEngineString(sv.name); // serverflags are for cross level information (sigils) G_Float(GBL_serverflags) = svs.serverflags; ED_LoadFromFile(sv.worldmodel->entities); sv.active = true; // all setup is completed, any further precache statements are errors sv.state = ss_active; // run two frames to allow everything to settle host_frametime = 0.1; SV_Physics(); SV_Physics(); // create a baseline for more efficient communications SV_CreateBaseline(); //johnfitz -- warn if signon buffer larger than standard server can handle if(sv.signon.cursize > 8000 - 2) //max size that will fit into 8000-sized client->message buffer with 2 extra bytes on the end Con_DWarning("%" PRIi32 " byte signon buffer exceeds standard limit of 7998 (max = %" PRIi32 ").\n", sv.signon.cursize, sv.signon.maxsize); //johnfitz // send serverinfo to all connected clients for(i = 0, host_client = svs.clients ; i < svs.maxclients ; i++, host_client++) if(host_client->active) SV_SendServerinfo(host_client); Con_DPrintf("Server spawned.\n"); }