/* 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 #include "quakedef.hpp" server_t sv; server_static_t svs; static char localmodels[MAX_MODELS][8]; // inline model names for precache int sv_protocol = PROTOCOL_FITZQUAKE; //johnfitz extern bool pr_alpha_supported; //johnfitz extern int pr_effects_mask; //============================================================================ /* =============== SV_Protocol_f =============== */ void SV_Protocol_f(void) { int i; switch (command::argc()) { case 1: console::info("\"sv_protocol\" is \"%i\"\n", sv_protocol); break; case 2: i = atoi(command::argv(1)->c_str()); if (i != PROTOCOL_NETQUAKE && i != PROTOCOL_FITZQUAKE && i != PROTOCOL_RMQ) console::info("sv_protocol must be %i or %i or %i\n", PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE, PROTOCOL_RMQ); else { sv_protocol = i; if (sv.active) console::info("changes will not take effect until the next level load.\n"); } break; default: console::safe_print("usage: sv_protocol \n"); break; } } /* =============== SV_Init =============== */ void SV_Init(void) { const char *p; extern convar sv_maxvelocity; extern convar sv_gravity; extern convar sv_nostep; extern convar sv_freezenonclients; extern convar sv_friction; extern convar sv_edgefriction; extern convar sv_stopspeed; extern convar sv_maxspeed; extern convar sv_accelerate; extern convar sv_idealpitchscale; extern convar sv_aim; extern convar sv_altnoclip; //johnfitz sv.edicts = NULL; // ericw -- sv.edicts switched to use malloc() sv_maxvelocity.inscribe(); sv_gravity.inscribe(); sv_friction.inscribe(); sv_gravity.set_callback(Host_Callback_Notify); sv_friction.set_callback(Host_Callback_Notify); sv_edgefriction.inscribe(); sv_stopspeed.inscribe(); sv_maxspeed.inscribe(); sv_maxspeed.set_callback(Host_Callback_Notify); sv_accelerate.inscribe(); sv_idealpitchscale.inscribe(); sv_aim.inscribe(); sv_nostep.inscribe(); sv_freezenonclients.inscribe(); sv_altnoclip.inscribe(); //johnfitz command::add("sv_protocol", &SV_Protocol_f); //johnfitz for (auto i = 0; i < MAX_MODELS; i++) sprintf(localmodels[i], "*%i", i); if (const auto i = common::check_param("-protocol"); i && i < com_argc - 1) sv_protocol = atoi(com_argv[i.value() + 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 %i. Accepted values: %i, %i, %i.", sv_protocol, PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE, PROTOCOL_RMQ); return; /* silence compiler */ } Sys_Printf("Server using protocol %i (%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, int color, int count) { int i, v; if (sv.datagram.size() > MAX_DATAGRAM - 18) return; sv.datagram.write_byte(svc_particle); sv.datagram.write_coord(org[0], static_cast(sv.protocolflags)); sv.datagram.write_coord(org[1], static_cast(sv.protocolflags)); sv.datagram.write_coord(org[2], static_cast(sv.protocolflags)); for (i = 0; i < 3; i++) { v = dir[i] * 16; if (v > 127) v = 127; else if (v < -128) v = -128; sv.datagram.write_char(v); } sv.datagram.write_byte(count); sv.datagram.write_byte(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 already 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, const char *sample, int volume, float attenuation) { int sound_num, ent; int i, field_mask; if (volume < 0 || volume > 255) Host_Error("SV_StartSound: volume = %i", volume); if (attenuation < 0 || attenuation > 4) Host_Error("SV_StartSound: attenuation = %f", attenuation); if (channel < 0 || channel > 7) Host_Error("SV_StartSound: channel = %i", channel); if (sv.datagram.size() > MAX_DATAGRAM - 21) 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]) { console::info("SV_StartSound: %s not precached\n", sample); return; } ent = NUM_FOR_EDICT(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 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 field_mask |= SND_LARGESOUND; } //johnfitz if (sv.datagram.size() > MAX_DATAGRAM - 21) return; // directed messages go only to the entity the are targeted on sv.datagram.write_byte(svc_sound); sv.datagram.write_byte(field_mask); if (field_mask & SND_VOLUME) sv.datagram.write_byte(volume); if (field_mask & SND_ATTENUATION) sv.datagram.write_byte(attenuation * 64); //johnfitz -- PROTOCOL_FITZQUAKE if (field_mask & SND_LARGEENTITY) { sv.datagram.write_short(ent); sv.datagram.write_byte(channel); } else sv.datagram.write_short((ent << 3) | channel); if (field_mask & SND_LARGESOUND) sv.datagram.write_short(sound_num); else sv.datagram.write_byte(sound_num); //johnfitz for (i = 0; i < 3; i++) sv.datagram.write_coord(entity->v.origin[i] + 0.5 * (entity->v.mins[i] + entity->v.maxs[i]), static_cast(sv.protocolflags)); } /* ================== SV_LocalSound - for 2021 rerelease ================== */ void SV_LocalSound(client_t *client, const char *sample) { int sound_num, field_mask; 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]) { console::info("SV_LocalSound: %s not precached\n", sample); return; } field_mask = 0; if (sound_num >= 256) { if (sv.protocol == PROTOCOL_NETQUAKE) return; field_mask = SND_LARGESOUND; } // TODO: replace maxsize call? // if (client->message.size() > client->message.maxsize - 4) // return; client->message.write_byte(svc_localsound); client->message.write_byte(field_mask); if (field_mask & SND_LARGESOUND) client->message.write_short(sound_num); else client->message.write_byte(sound_num); } /* ============================================================================== CLIENT SPAWNING ============================================================================== */ static bool SV_IsLocalClient(client_t *client) { return std::strcmp(NET_QSocketGetAddressString(client->netconnection), "LOCAL") == 0; } /* ================ 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]; int i; //johnfitz client->message.write_byte(svc_print); sprintf(message, "%c\nFITZQUAKE %1.2f SERVER (%i CRC)\n", 2, FITZQUAKE_VERSION, pr_crc); //johnfitz -- include fitzquake version client->message.write_string(message); client->message.write_byte(svc_serverinfo); client->message.write_long(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 client->message.write_long(sv.protocolflags); } client->message.write_byte(svs.maxclients); if (!coop.value && deathmatch.value) client->message.write_byte(GAME_DEATHMATCH); else client->message.write_byte(GAME_COOP); client->message.write_string(PR_GetString(sv.edicts->v.message)); //johnfitz -- only send the first 256 model and sound precaches if protocol is 15 for (i = 1, s = sv.model_precache + 1; *s; s++, i++) if (sv.protocol != PROTOCOL_NETQUAKE || i < 256) client->message.write_string(*s); client->message.write_byte(0); for (i = 1, s = sv.sound_precache + 1; *s; s++, i++) if (sv.protocol != PROTOCOL_NETQUAKE || i < 256) client->message.write_string(*s); client->message.write_byte(0); //johnfitz // send music client->message.write_byte(svc_cdtrack); client->message.write_byte(sv.edicts->v.sounds); client->message.write_byte(sv.edicts->v.sounds); // set view client->message.write_byte(svc_setview); client->message.write_short(NUM_FOR_EDICT(client->edict)); client->message.write_byte(svc_signonnum); client->message.write_byte(1); client->sendsignon = PRESPAWN_FLUSH; 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(int clientnum) { edict_t *ent; client_t *client; int edictnum; struct qsocket_s *netconnection; int i; float spawn_parms[NUM_SPAWN_PARMS]; client = svs.clients + clientnum; console::debug("Client %s connected\n", NET_QSocketGetAddressString(client->netconnection)); edictnum = clientnum + 1; ent = EDICT_NUM(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 = net::msg{}; 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(pr_global_struct->SetNewParms); for (i = 0; i < NUM_SPAWN_PARMS; i++) client->spawn_parms[i] = (&pr_global_struct->parm1)[i]; } SV_SendServerinfo(client); } /* =================== SV_CheckForNewClients =================== */ void SV_CheckForNewClients(void) { struct qsocket_s *ret; int 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) { sv.datagram.clear(); } /* ============================================================================= 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 int fatbytes; static byte *fatpvs; static int fatpvs_capacity; void SV_AddToFatPVS(vec3_t org, mnode_t *node, qmodel_t *worldmodel) //johnfitz -- added worldmodel as a parameter { int 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 %d bytes", fatpvs_capacity); } std::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; int i; VectorAdd(client->v.origin, client->v.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, net::msg &msg) { int e, i; int bits; byte *pvs; vec3_t org; float miss; edict_t *ent; eval_t *val; // find the client's PVS VectorAdd(clent->v.origin, clent->v.view_ofs, org); pvs = SV_FatPVS(org, sv.worldmodel); // send over all entities (excpet the client) that touch the pvs ent = NEXT_EDICT(sv.edicts); for (e = 1; e < sv.num_edicts; e++, ent = NEXT_EDICT(ent)) { if (ent != clent) // clent is ALLWAYS sent { // ignore ents without visible models if (!ent->v.modelindex || !PR_GetString(ent->v.model)[0]) continue; //johnfitz -- don't send model>255 entities if protocol is 15 if (sv.protocol == PROTOCOL_NETQUAKE && (int) ent->v.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. // For float coords and angles the limit is 40. // FIXME: Use tighter limit according to protocol flags and send bits. // if (msg->cursize + 40 > msg->maxsize) { // //johnfitz -- less spammy overflow message // if (!dev_overflows.packetsize || dev_overflows.packetsize + CONSOLE_RESPAM_TIME < realtime) { // console::info("Packet overflow!\n"); // dev_overflows.packetsize = realtime; // } // goto stats; // //johnfitz // } // send an update bits = 0; for (i = 0; i < 3; i++) { miss = ent->v.origin[i] - ent->baseline.origin[i]; if (miss < -0.1 || miss > 0.1) bits |= U_ORIGIN1 << i; } if (ent->v.angles[0] != ent->baseline.angles[0]) bits |= U_ANGLE1; if (ent->v.angles[1] != ent->baseline.angles[1]) bits |= U_ANGLE2; if (ent->v.angles[2] != ent->baseline.angles[2]) bits |= U_ANGLE3; if (ent->v.movetype == MOVETYPE_STEP) bits |= U_STEP; // don't mess up the step animation if (ent->baseline.colormap != ent->v.colormap) bits |= U_COLORMAP; if (ent->baseline.skin != ent->v.skin) bits |= U_SKIN; if (ent->baseline.frame != ent->v.frame) bits |= U_FRAME; if ((ent->baseline.effects ^ (int) ent->v.effects) & pr_effects_mask) bits |= U_EFFECTS; if (ent->baseline.modelindex != ent->v.modelindex) bits |= U_MODEL; //johnfitz -- alpha if (pr_alpha_supported) { // TODO: find a cleaner place to put this code val = GetEdictFieldValue(ent, "alpha"); if (val) ent->alpha = ENTALPHA_ENCODE(val->_float); } //don't send invisible entities unless they have effects if (ent->alpha == ENTALPHA_ZERO && !((int) ent->v.effects & pr_effects_mask)) continue; //johnfitz val = GetEdictFieldValue(ent, "scale"); if (val) ent->scale = ENTSCALE_ENCODE(val->_float); else ent->scale = ENTSCALE_DEFAULT; //johnfitz -- PROTOCOL_FITZQUAKE if (sv.protocol != PROTOCOL_NETQUAKE) { if (ent->baseline.alpha != ent->alpha) bits |= U_ALPHA; if (ent->baseline.scale != ent->scale) bits |= U_SCALE; if (bits & U_FRAME && (int) ent->v.frame & 0xFF00) bits |= U_FRAME2; if (bits & U_MODEL && (int) ent->v.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.write_byte(bits | U_SIGNAL); if (bits & U_MOREBITS) msg.write_byte(bits >> 8); //johnfitz -- PROTOCOL_FITZQUAKE if (bits & U_EXTEND1) msg.write_byte(bits >> 16); if (bits & U_EXTEND2) msg.write_byte(bits >> 24); //johnfitz if (bits & U_LONGENTITY) msg.write_short(e); else msg.write_byte(e); if (bits & U_MODEL) msg.write_byte(ent->v.modelindex); if (bits & U_FRAME) msg.write_byte(ent->v.frame); if (bits & U_COLORMAP) msg.write_byte(ent->v.colormap); if (bits & U_SKIN) msg.write_byte(ent->v.skin); if (bits & U_EFFECTS) msg.write_byte((int) ent->v.effects & pr_effects_mask); if (bits & U_ORIGIN1) msg.write_coord(ent->v.origin[0], static_cast(sv.protocolflags)); if (bits & U_ANGLE1) msg.write_angle(ent->v.angles[0], static_cast(sv.protocolflags)); if (bits & U_ORIGIN2) msg.write_coord(ent->v.origin[1], static_cast(sv.protocolflags)); if (bits & U_ANGLE2) msg.write_angle(ent->v.angles[1], static_cast(sv.protocolflags)); if (bits & U_ORIGIN3) msg.write_coord(ent->v.origin[2], static_cast(sv.protocolflags)); if (bits & U_ANGLE3) msg.write_angle(ent->v.angles[2], static_cast(sv.protocolflags)); //johnfitz -- PROTOCOL_FITZQUAKE if (bits & U_ALPHA) msg.write_byte(ent->alpha); if (bits & U_SCALE) msg.write_byte(ent->scale); if (bits & U_FRAME2) msg.write_byte((int) ent->v.frame >> 8); if (bits & U_MODEL2) msg.write_byte((int) ent->v.modelindex >> 8); if (bits & U_LERPFINISH) msg.write_byte((byte) (Q_rint((ent->v.nextthink-sv.time)*255))); //johnfitz } //johnfitz -- devstats stats: if (msg.size() > 1024 && dev_peakstats.packetsize <= 1024) console::dev_warn("%i byte packet exceeds standard limit of 1024 (max = %d).\n", msg.size(), 1024); // TODO dev_stats.packetsize = msg.size(); dev_peakstats.packetsize = std::max(static_cast(msg.size()), dev_peakstats.packetsize); //johnfitz } /* ============= SV_CleanupEnts ============= */ void SV_CleanupEnts(void) { int e; edict_t *ent; ent = NEXT_EDICT(sv.edicts); for (e = 1; e < sv.num_edicts; e++, ent = NEXT_EDICT(ent)) { ent->v.effects = (int) ent->v.effects & ~EF_MUZZLEFLASH; } } /* ================== SV_WriteClientdataToMessage ================== */ void SV_WriteClientdataToMessage(edict_t *ent, net::msg &msg) { int bits; int i; edict_t *other; int items; eval_t *val; // // send a damage message // if (ent->v.dmg_take || ent->v.dmg_save) { other = PROG_TO_EDICT(ent->v.dmg_inflictor); msg.write_byte(svc_damage); msg.write_byte(ent->v.dmg_save); msg.write_byte(ent->v.dmg_take); for (i = 0; i < 3; i++) msg.write_coord(other->v.origin[i] + 0.5 * (other->v.mins[i] + other->v.maxs[i]), static_cast(sv.protocolflags)); ent->v.dmg_take = 0; ent->v.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 (ent->v.fixangle) { msg.write_byte(svc_setangle); for (i = 0; i < 3; i++) msg.write_angle(ent->v.angles[i], static_cast(sv.protocolflags)); ent->v.fixangle = 0; } bits = 0; if (ent->v.view_ofs[2] != DEFAULT_VIEWHEIGHT) bits |= SU_VIEWHEIGHT; if (ent->v.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 = (int) ent->v.items | ((int) val->_float << 23); else items = (int) ent->v.items | ((int) pr_global_struct->serverflags << 28); bits |= SU_ITEMS; if ((int) ent->v.flags & FL_ONGROUND) bits |= SU_ONGROUND; if (ent->v.waterlevel >= 2) bits |= SU_INWATER; for (i = 0; i < 3; i++) { if (ent->v.punchangle[i]) bits |= (SU_PUNCH1 << i); if (ent->v.velocity[i]) bits |= (SU_VELOCITY1 << i); } if (ent->v.weaponframe) bits |= SU_WEAPONFRAME; if (ent->v.armorvalue) bits |= SU_ARMOR; // if (ent->v.weapon) bits |= SU_WEAPON; //johnfitz -- PROTOCOL_FITZQUAKE if (sv.protocol != PROTOCOL_NETQUAKE) { if (bits & SU_WEAPON && SV_ModelIndex(PR_GetString(ent->v.weaponmodel)) & 0xFF00) bits |= SU_WEAPON2; if ((int) ent->v.armorvalue & 0xFF00) bits |= SU_ARMOR2; if ((int) ent->v.currentammo & 0xFF00) bits |= SU_AMMO2; if ((int) ent->v.ammo_shells & 0xFF00) bits |= SU_SHELLS2; if ((int) ent->v.ammo_nails & 0xFF00) bits |= SU_NAILS2; if ((int) ent->v.ammo_rockets & 0xFF00) bits |= SU_ROCKETS2; if ((int) ent->v.ammo_cells & 0xFF00) bits |= SU_CELLS2; if (bits & SU_WEAPONFRAME && (int) ent->v.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.write_byte(svc_clientdata); msg.write_short(bits); //johnfitz -- PROTOCOL_FITZQUAKE if (bits & SU_EXTEND1) msg.write_byte(bits >> 16); if (bits & SU_EXTEND2) msg.write_byte(bits >> 24); //johnfitz if (bits & SU_VIEWHEIGHT) msg.write_char(ent->v.view_ofs[2]); if (bits & SU_IDEALPITCH) msg.write_char(ent->v.idealpitch); for (i = 0; i < 3; i++) { if (bits & (SU_PUNCH1 << i)) msg.write_char(ent->v.punchangle[i]); if (bits & (SU_VELOCITY1 << i)) msg.write_char(ent->v.velocity[i] / 16); } // [always sent] if (bits & SU_ITEMS) msg.write_long(items); if (bits & SU_WEAPONFRAME) msg.write_byte(ent->v.weaponframe); if (bits & SU_ARMOR) msg.write_byte(ent->v.armorvalue); if (bits & SU_WEAPON) msg.write_byte(SV_ModelIndex(PR_GetString(ent->v.weaponmodel))); msg.write_short(ent->v.health); msg.write_byte(ent->v.currentammo); msg.write_byte(ent->v.ammo_shells); msg.write_byte(ent->v.ammo_nails); msg.write_byte(ent->v.ammo_rockets); msg.write_byte(ent->v.ammo_cells); if (standard_quake) { msg.write_byte(ent->v.weapon); } else { for (i = 0; i < 32; i++) { if (((int) ent->v.weapon) & (1 << i)) { msg.write_byte(i); break; } } } //johnfitz -- PROTOCOL_FITZQUAKE if (bits & SU_WEAPON2) msg.write_byte(SV_ModelIndex(PR_GetString(ent->v.weaponmodel)) >> 8); if (bits & SU_ARMOR2) msg.write_byte((int) ent->v.armorvalue >> 8); if (bits & SU_AMMO2) msg.write_byte((int) ent->v.currentammo >> 8); if (bits & SU_SHELLS2) msg.write_byte((int) ent->v.ammo_shells >> 8); if (bits & SU_NAILS2) msg.write_byte((int) ent->v.ammo_nails >> 8); if (bits & SU_ROCKETS2) msg.write_byte((int) ent->v.ammo_rockets >> 8); if (bits & SU_CELLS2) msg.write_byte((int) ent->v.ammo_cells >> 8); if (bits & SU_WEAPONFRAME2) msg.write_byte((int) ent->v.weaponframe >> 8); if (bits & SU_WEAPONALPHA) msg.write_byte(ent->alpha); //for now, weaponalpha = client entity alpha //johnfitz } /* ======================= SV_SendClientDatagram ======================= */ bool SV_SendClientDatagram(client_t *client) { byte buf[MAX_DATAGRAM]; net::msg msg{}; //johnfitz -- if client is nonlocal, use smaller max size so packets aren't fragmented // if (std::strcmp(NET_QSocketGetAddressString(client->netconnection), "LOCAL") != 0) // msg.maxsize = DATAGRAM_MTU; //johnfitz msg.write_byte(svc_time); msg.write_float(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) msg.write(sv.datagram.data(), sv.datagram.size()); // 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) { int 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 != host_client->edict->v.frags) { for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (!client->active) continue; client->message.write_byte(svc_updatefrags); client->message.write_byte(i); client->message.write_short(host_client->edict->v.frags); } host_client->old_frags = host_client->edict->v.frags; } } for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (!client->active) continue; client->message.write(sv.reliable_datagram.data(), sv.reliable_datagram.size()); } sv.reliable_datagram.clear(); } /* ======================= SV_SendNop Send a nop message without trashing or sending the accumulated client message buffer ======================= */ void SV_SendNop(client_t *client) { net::msg msg{}; msg.write_char(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) { int 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 } if (host_client->sendsignon == PRESPAWN_SIGNONBUFS) { bool local = SV_IsLocalClient(host_client); while (host_client->signonidx < sv.signons.size()) { auto signon = sv.signons[host_client->signonidx]; // if (host_client->message.cursize + signon->cursize > host_client->message.maxsize) // break; host_client->message.write(signon.data(), signon.size()); host_client->signonidx++; // only send multiple buffers at once when playing locally, // otherwise we send one signon at a time to avoid overflowing // the datagram buffer for clients using a lower limit (e.g. 32000 in QS) if (!local) break; } if (host_client->signonidx == sv.signons.size()) host_client->sendsignon = PRESPAWN_SIGNONMSG; } if (host_client->sendsignon == PRESPAWN_SIGNONMSG) { //if (host_client->message.siz + 2 < host_client->message.maxsize) { host_client->message.write_byte(svc_signonnum); host_client->message.write_byte(2); host_client->sendsignon = PRESPAWN_FLUSH; //} } } // 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.empty() || 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 host_client->message.clear(); host_client->last_message = realtime; if (host_client->sendsignon == PRESPAWN_FLUSH) host_client->sendsignon = PRESPAWN_DONE; } } } // clear muzzle flashes SV_CleanupEnts(); } /* ============================================================================== SERVER SPAWNING ============================================================================== */ #define SIGNON_SIZE 31500 // QS has a MAX_DATAGRAM of 32000, try to play nice /* ================ SV_AddSignonBuffer ================ */ static void SV_AddSignonBuffer(void) { if (sv.signons.size() >= MAX_SIGNON_BUFFERS) Host_Error("SV_AddSignonBuffer overflow\n"); sv.signons.emplace_back(); sv.current_signon = sv.signons.size() - 1; } /* ================ SV_ReserveSignonSpace ================ */ void SV_ReserveSignonSpace(int numbytes) { if (sv.signons[sv.current_signon].size() + numbytes > SIGNON_SIZE) SV_AddSignonBuffer(); } /* ================ SV_ModelIndex ================ */ int SV_ModelIndex(const char *name) { int 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) { int i; edict_t *svent; int entnum; int bits; //johnfitz -- PROTOCOL_FITZQUAKE for (entnum = 0; entnum < sv.num_edicts; entnum++) { // get the current server version svent = EDICT_NUM(entnum); if (svent->free) continue; if (entnum > svs.maxclients && !svent->v.modelindex) continue; // // create entity baseline // VectorCopy(svent->v.origin, svent->baseline.origin); VectorCopy(svent->v.angles, svent->baseline.angles); svent->baseline.frame = svent->v.frame; svent->baseline.skin = svent->v.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 svent->baseline.scale = ENTSCALE_DEFAULT; } else { svent->baseline.colormap = 0; svent->baseline.modelindex = SV_ModelIndex(PR_GetString(svent->v.model)); svent->baseline.alpha = svent->alpha; //johnfitz -- alpha support svent->baseline.scale = ENTSCALE_DEFAULT; if (sv.protocol == PROTOCOL_RMQ) { eval_t *val; val = GetEdictFieldValue(svent, "scale"); if (val) svent->baseline.scale = ENTSCALE_ENCODE(val->_float); } } //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; svent->baseline.scale = ENTSCALE_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; if (svent->baseline.scale != ENTSCALE_DEFAULT) bits |= B_SCALE; } //johnfitz // // add to the message // SV_ReserveSignonSpace(35); //johnfitz -- PROTOCOL_FITZQUAKE if (bits) sv.signons[sv.current_signon].write_byte(svc_spawnbaseline2); else sv.signons[sv.current_signon].write_byte(svc_spawnbaseline); //johnfitz sv.signons[sv.current_signon].write_short(entnum); //johnfitz -- PROTOCOL_FITZQUAKE if (bits) sv.signons[sv.current_signon].write_byte(bits); if (bits & B_LARGEMODEL) sv.signons[sv.current_signon].write_short(svent->baseline.modelindex); else sv.signons[sv.current_signon].write_byte(svent->baseline.modelindex); if (bits & B_LARGEFRAME) sv.signons[sv.current_signon].write_short(svent->baseline.frame); else sv.signons[sv.current_signon].write_byte(svent->baseline.frame); //johnfitz sv.signons[sv.current_signon].write_byte(svent->baseline.colormap); sv.signons[sv.current_signon].write_byte(svent->baseline.skin); for (i = 0; i < 3; i++) { sv.signons[sv.current_signon].write_coord(svent->baseline.origin[i], static_cast(sv.protocolflags)); sv.signons[sv.current_signon].write_angle(svent->baseline.angles[i], static_cast(sv.protocolflags)); } //johnfitz -- PROTOCOL_FITZQUAKE if (bits & B_ALPHA) sv.signons[sv.current_signon].write_byte(svent->baseline.alpha); //johnfitz if (bits & B_SCALE) sv.signons[sv.current_signon].write_byte(svent->baseline.scale); } } /* ================ SV_SendReconnect Tell all the clients that the server is changing levels ================ */ void SV_SendReconnect(void) { byte data[128]; net::msg msg{}; msg.write_char( svc_stufftext); msg.write_string("reconnect\n"); NET_SendToAll(msg, 5.0); if (!isDedicated) command::execute_string("reconnect\n", command::source::command); } /* ================ SV_SaveSpawnparms Grabs the current state of each client for saving across the transition to another level ================ */ void SV_SaveSpawnparms(void) { int i, j; svs.serverflags = pr_global_struct->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 pr_global_struct->self = EDICT_TO_PROG(host_client->edict); PR_ExecuteProgram(pr_global_struct->SetChangeParms); for (j = 0; j < NUM_SPAWN_PARMS; j++) host_client->spawn_parms[j] = (&pr_global_struct->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; int i, signonsize; // let's not have any servers with no name if (hostname.string[0] == 0) convar::set("hostname", "UNNAMED"); scr_centertime_off = 0; console::debug("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) convar::set("deathmatch", "0"); current_skill = (int) (skill.value + 0.5); if (current_skill < 0) current_skill = 0; if (current_skill > 3) current_skill = 3; convar::set_value("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 = std::clamp((int) max_edicts.value,MIN_EDICTS, 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 = net::msg{}; sv.reliable_datagram = net::msg{}; SV_AddSignonBuffer(); // 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 = EDICT_NUM(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) { console::info("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 = EDICT_NUM(0); memset(&ent->v, 0, progs->entityfields * 4); ent->free = false; ent->v.model = PR_SetEngineString(sv.worldmodel->name); ent->v.modelindex = 1; // world model ent->v.solid = SOLID_BSP; ent->v.movetype = MOVETYPE_PUSH; if (coop.value) pr_global_struct->coop = coop.value; else pr_global_struct->deathmatch = deathmatch.value; pr_global_struct->mapname = PR_SetEngineString(sv.name); // serverflags are for cross level information (sigils) pr_global_struct->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 for (i = 0, signonsize = 0; i < sv.signons.size(); i++) signonsize += sv.signons[i].size(); if (signonsize > 64000 - 2) console::dev_warn("%i byte signon buffer exceeds QS limit of 63998.\n", signonsize); else if (signonsize > 8000 - 2) //max size that will fit into 8000-sized client->message buffer with 2 extra bytes on the end console::dev_warn("%i byte signon buffer exceeds standard limit of 7998.\n", signonsize); //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); console::debug("Server spawned.\n"); }