diff options
Diffstat (limited to 'src/jake2/server/SV_ENTS.java')
-rw-r--r-- | src/jake2/server/SV_ENTS.java | 1203 |
1 files changed, 610 insertions, 593 deletions
diff --git a/src/jake2/server/SV_ENTS.java b/src/jake2/server/SV_ENTS.java index 8b3db67..706fc27 100644 --- a/src/jake2/server/SV_ENTS.java +++ b/src/jake2/server/SV_ENTS.java @@ -1,603 +1,620 @@ /* -Copyright (C) 1997-2001 Id Software, Inc. - -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. - -*/ + * Copyright (C) 1997-2001 Id Software, Inc. + * + * 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. + * + */ // Created on 17.01.2004 by RST. -// $Id: SV_ENTS.java,v 1.4 2004-08-29 21:39:25 hzi Exp $ - +// $Id: SV_ENTS.java,v 1.5 2004-09-22 19:22:12 salomo Exp $ package jake2.server; +import jake2.Defines; import jake2.game.*; import jake2.qcommon.*; +import jake2.util.Math3D; import java.io.IOException; -public class SV_ENTS extends SV_USER { - - /* - ============================================================================= - - Encode a client frame onto the network channel - - ============================================================================= - */ - - /* - ============= - SV_EmitPacketEntities - - Writes a delta update of an entity_state_t list to the message. - ============= - */ - static void SV_EmitPacketEntities(client_frame_t from, client_frame_t to, sizebuf_t msg) { - entity_state_t oldent = null, newent = null; - int oldindex, newindex; - int oldnum, newnum; - int from_num_entities; - int bits; - - MSG.WriteByte(msg, svc_packetentities); - - if (from == null) - from_num_entities = 0; - else - from_num_entities = from.num_entities; - - newindex = 0; - oldindex = 0; - while (newindex < to.num_entities || oldindex < from_num_entities) { - if (newindex >= to.num_entities) - newnum = 9999; - else { - newent = svs.client_entities[(to.first_entity + newindex) % svs.num_client_entities]; - newnum = newent.number; - } - - if (oldindex >= from_num_entities) - oldnum = 9999; - else { - oldent = svs.client_entities[(from.first_entity + oldindex) % svs.num_client_entities]; - oldnum = oldent.number; - } - - if (newnum == oldnum) { // delta update from old position - // because the force parm is false, this will not result - // in any bytes being emited if the entity has not changed at all - // note that players are always 'newentities', this updates their oldorigin always - // and prevents warping - MSG.WriteDeltaEntity(oldent, newent, msg, false, newent.number <= maxclients.value); - oldindex++; - newindex++; - continue; - } - - if (newnum < oldnum) { // this is a new entity, send it from the baseline - MSG.WriteDeltaEntity(sv.baselines[newnum], newent, msg, true, true); - newindex++; - continue; - } - - if (newnum > oldnum) { // the old entity isn't present in the new message - bits = U_REMOVE; - if (oldnum >= 256) - bits |= U_NUMBER16 | U_MOREBITS1; - - MSG.WriteByte(msg, bits & 255); - if ((bits & 0x0000ff00) != 0) - MSG.WriteByte(msg, (bits >> 8) & 255); - - if ((bits & U_NUMBER16) != 0) - MSG.WriteShort(msg, oldnum); - else - MSG.WriteByte(msg, oldnum); - - oldindex++; - continue; - } - } - - MSG.WriteShort(msg, 0); // end of packetentities - - } - - /* - ============= - SV_WritePlayerstateToClient - - ============= - */ - static void SV_WritePlayerstateToClient(client_frame_t from, client_frame_t to, sizebuf_t msg) { - int i; - int pflags; - // ptr - player_state_t ps, ops; - // mem - player_state_t dummy; - int statbits; - - ps = to.ps; - if (from == null) { - //memset (dummy, 0, sizeof(dummy)); - dummy = new player_state_t(); - ops = dummy; - } - else - ops = from.ps; - - // - // determine what needs to be sent - // - pflags = 0; - - if (ps.pmove.pm_type != ops.pmove.pm_type) - pflags |= PS_M_TYPE; - - if (ps.pmove.origin[0] != ops.pmove.origin[0] - || ps.pmove.origin[1] != ops.pmove.origin[1] - || ps.pmove.origin[2] != ops.pmove.origin[2]) - pflags |= PS_M_ORIGIN; - - if (ps.pmove.velocity[0] != ops.pmove.velocity[0] - || ps.pmove.velocity[1] != ops.pmove.velocity[1] - || ps.pmove.velocity[2] != ops.pmove.velocity[2]) - pflags |= PS_M_VELOCITY; - - if (ps.pmove.pm_time != ops.pmove.pm_time) - pflags |= PS_M_TIME; - - if (ps.pmove.pm_flags != ops.pmove.pm_flags) - pflags |= PS_M_FLAGS; - - if (ps.pmove.gravity != ops.pmove.gravity) - pflags |= PS_M_GRAVITY; - - if (ps.pmove.delta_angles[0] != ops.pmove.delta_angles[0] - || ps.pmove.delta_angles[1] != ops.pmove.delta_angles[1] - || ps.pmove.delta_angles[2] != ops.pmove.delta_angles[2]) - pflags |= PS_M_DELTA_ANGLES; - - if (ps.viewoffset[0] != ops.viewoffset[0] || ps.viewoffset[1] != ops.viewoffset[1] || ps.viewoffset[2] != ops.viewoffset[2]) - pflags |= PS_VIEWOFFSET; - - if (ps.viewangles[0] != ops.viewangles[0] || ps.viewangles[1] != ops.viewangles[1] || ps.viewangles[2] != ops.viewangles[2]) - pflags |= PS_VIEWANGLES; - - if (ps.kick_angles[0] != ops.kick_angles[0] - || ps.kick_angles[1] != ops.kick_angles[1] - || ps.kick_angles[2] != ops.kick_angles[2]) - pflags |= PS_KICKANGLES; - - if (ps.blend[0] != ops.blend[0] || ps.blend[1] != ops.blend[1] || ps.blend[2] != ops.blend[2] || ps.blend[3] != ops.blend[3]) - pflags |= PS_BLEND; - - if (ps.fov != ops.fov) - pflags |= PS_FOV; - - if (ps.rdflags != ops.rdflags) - pflags |= PS_RDFLAGS; - - if (ps.gunframe != ops.gunframe) - pflags |= PS_WEAPONFRAME; - - pflags |= PS_WEAPONINDEX; - - // - // write it - // - MSG.WriteByte(msg, svc_playerinfo); - MSG.WriteShort(msg, pflags); - - // - // write the pmove_state_t - // - if ((pflags & PS_M_TYPE) != 0) - MSG.WriteByte(msg, ps.pmove.pm_type); - - if ((pflags & PS_M_ORIGIN) != 0) { - MSG.WriteShort(msg, ps.pmove.origin[0]); - MSG.WriteShort(msg, ps.pmove.origin[1]); - MSG.WriteShort(msg, ps.pmove.origin[2]); - } - - if ((pflags & PS_M_VELOCITY) != 0) { - MSG.WriteShort(msg, ps.pmove.velocity[0]); - MSG.WriteShort(msg, ps.pmove.velocity[1]); - MSG.WriteShort(msg, ps.pmove.velocity[2]); - } - - if ((pflags & PS_M_TIME) != 0) - MSG.WriteByte(msg, ps.pmove.pm_time); - - if ((pflags & PS_M_FLAGS) != 0) - MSG.WriteByte(msg, ps.pmove.pm_flags); - - if ((pflags & PS_M_GRAVITY) != 0) - MSG.WriteShort(msg, ps.pmove.gravity); - - if ((pflags & PS_M_DELTA_ANGLES) != 0) { - MSG.WriteShort(msg, ps.pmove.delta_angles[0]); - MSG.WriteShort(msg, ps.pmove.delta_angles[1]); - MSG.WriteShort(msg, ps.pmove.delta_angles[2]); - } - - // - // write the rest of the player_state_t - // - if ((pflags & PS_VIEWOFFSET) != 0) { - MSG.WriteChar(msg, ps.viewoffset[0] * 4); - MSG.WriteChar(msg, ps.viewoffset[1] * 4); - MSG.WriteChar(msg, ps.viewoffset[2] * 4); - } - - if ((pflags & PS_VIEWANGLES) != 0) { - MSG.WriteAngle16(msg, ps.viewangles[0]); - MSG.WriteAngle16(msg, ps.viewangles[1]); - MSG.WriteAngle16(msg, ps.viewangles[2]); - } - - if ((pflags & PS_KICKANGLES) != 0) { - MSG.WriteChar(msg, ps.kick_angles[0] * 4); - MSG.WriteChar(msg, ps.kick_angles[1] * 4); - MSG.WriteChar(msg, ps.kick_angles[2] * 4); - } - - if ((pflags & PS_WEAPONINDEX) != 0) { - MSG.WriteByte(msg, ps.gunindex); - } - - if ((pflags & PS_WEAPONFRAME) != 0) { - MSG.WriteByte(msg, ps.gunframe); - MSG.WriteChar(msg, ps.gunoffset[0] * 4); - MSG.WriteChar(msg, ps.gunoffset[1] * 4); - MSG.WriteChar(msg, ps.gunoffset[2] * 4); - MSG.WriteChar(msg, ps.gunangles[0] * 4); - MSG.WriteChar(msg, ps.gunangles[1] * 4); - MSG.WriteChar(msg, ps.gunangles[2] * 4); - } - - if ((pflags & PS_BLEND) != 0) { - MSG.WriteByte(msg, ps.blend[0] * 255); - MSG.WriteByte(msg, ps.blend[1] * 255); - MSG.WriteByte(msg, ps.blend[2] * 255); - MSG.WriteByte(msg, ps.blend[3] * 255); - } - if ((pflags & PS_FOV) != 0) - MSG.WriteByte(msg, ps.fov); - if ((pflags & PS_RDFLAGS) != 0) - MSG.WriteByte(msg, ps.rdflags); - - // send stats - statbits = 0; - for (i = 0; i < MAX_STATS; i++) - if (ps.stats[i] != ops.stats[i]) - statbits |= 1 << i; - MSG.WriteLong(msg, statbits); - for (i = 0; i < MAX_STATS; i++) - if ((statbits & (1 << i)) != 0) - MSG.WriteShort(msg, ps.stats[i]); - } - - /* - ================== - SV_WriteFrameToClient - ================== - */ - public static void SV_WriteFrameToClient(client_t client, sizebuf_t msg) { - //ptr - client_frame_t frame, oldframe; - int lastframe; - - //Com.Printf ("%i . %i\n", new Vargs().add(client.lastframe).add(sv.framenum)); - // this is the frame we are creating - frame = client.frames[sv.framenum & UPDATE_MASK]; - if (client.lastframe <= 0) { // client is asking for a retransmit - oldframe = null; - lastframe = -1; - } - else if ( - sv.framenum - client.lastframe >= (UPDATE_BACKUP - 3)) { // client hasn't gotten a good message through in a long time - // Com_Printf ("%s: Delta request from out-of-date packet.\n", client.name); - oldframe = null; - lastframe = -1; - } - else { // we have a valid message to delta from - oldframe = client.frames[client.lastframe & UPDATE_MASK]; - lastframe = client.lastframe; - } - - MSG.WriteByte(msg, svc_frame); - MSG.WriteLong(msg, sv.framenum); - MSG.WriteLong(msg, lastframe); // what we are delta'ing from - MSG.WriteByte(msg, client.surpressCount); // rate dropped packets - client.surpressCount = 0; - - // send over the areabits - MSG.WriteByte(msg, frame.areabytes); - SZ.Write(msg, frame.areabits, frame.areabytes); - - // delta encode the playerstate - SV_WritePlayerstateToClient(oldframe, frame, msg); - - // delta encode the entities - SV_EmitPacketEntities(oldframe, frame, msg); - } - - /* - ============================================================================= - - Build a client frame structure - - ============================================================================= - */ - - static byte fatpvs[] = new byte[65536 / 8]; // 32767 is MAX_MAP_LEAFS - - /* - ============ - SV_FatPVS - - The client will interpolate the view position, - so we can't use a single PVS point - =========== - */ - public static void SV_FatPVS(float[] org) { - int leafs[] = new int[64]; - int i, j, count; - int longs; - byte src[]; - float[] mins = { 0, 0, 0 }, maxs = { 0, 0, 0 }; - - for (i = 0; i < 3; i++) { - mins[i] = org[i] - 8; - maxs[i] = org[i] + 8; - } - - count = CM.CM_BoxLeafnums(mins, maxs, leafs, 64, null); - - if (count < 1) - Com.Error(ERR_FATAL, "SV_FatPVS: count < 1"); - - longs = (CM.CM_NumClusters() + 31) >> 5; - - // convert leafs to clusters - for (i = 0; i < count; i++) - leafs[i] = CM.CM_LeafCluster(leafs[i]); - - System.arraycopy(CM.CM_ClusterPVS(leafs[0]), 0, fatpvs, 0, longs << 2); - // or in all the other leaf bits - for (i = 1; i < count; i++) { - for (j = 0; j < i; j++) - if (leafs[i] == leafs[j]) - break; - if (j != i) - continue; // already have the cluster we want - - src = CM.CM_ClusterPVS(leafs[i]); - - //for (j=0 ; j<longs ; j++) - // ((long *)fatpvs)[j] |= ((long *)src)[j]; - int k=0; - for (j = 0; j < longs; j++) { - fatpvs[k] |= src[k++]; - fatpvs[k] |= src[k++]; - fatpvs[k] |= src[k++]; - fatpvs[k] |= src[k++]; - } - } - } - - /* - ============= - SV_BuildClientFrame - - Decides which entities are going to be visible to the client, and - copies off the playerstat and areabits. - ============= - */ - public static void SV_BuildClientFrame(client_t client) { - int e, i; - float[] org = { 0, 0, 0 }; - edict_t ent; - edict_t clent; - client_frame_t frame; - entity_state_t state; - int l; - int clientarea, clientcluster; - int leafnum; - int c_fullsend; - byte clientphs[]; - byte bitvector[]; - - clent = client.edict; - if (clent.client == null) - return; // not in game yet - - // this is the frame we are creating - frame = client.frames[sv.framenum & UPDATE_MASK]; - - frame.senttime = svs.realtime; // save it for ping calc later - - // find the client's PVS - for (i = 0; i < 3; i++) - org[i] = clent.client.ps.pmove.origin[i] * 0.125f + clent.client.ps.viewoffset[i]; - - leafnum = CM.CM_PointLeafnum(org); - clientarea = CM.CM_LeafArea(leafnum); - clientcluster = CM.CM_LeafCluster(leafnum); - - // calculate the visible areas - frame.areabytes = CM.CM_WriteAreaBits(frame.areabits, clientarea); - - // grab the current player_state_t - frame.ps.set(clent.client.ps); - - SV_FatPVS(org); - clientphs = CM.CM_ClusterPHS(clientcluster); - - // build up the list of visible entities - frame.num_entities = 0; - frame.first_entity = svs.next_client_entities; - - c_fullsend = 0; - - for (e = 1; e < GameBase.num_edicts; e++) { - ent = GameBase.g_edicts[e]; - - // ignore ents without visible models - if ((ent.svflags & SVF_NOCLIENT) != 0) - continue; - - // ignore ents without visible models unless they have an effect - if (0 == ent.s.modelindex && 0 == ent.s.effects && 0 == ent.s.sound && 0 == ent.s.event) - continue; - - // ignore if not touching a PV leaf - if (ent != clent) { - // check area - if (!CM.CM_AreasConnected(clientarea, ent.areanum)) { // doors can legally straddle two areas, so - // we may need to check another one - if (0 == ent.areanum2 || !CM.CM_AreasConnected(clientarea, ent.areanum2)) - continue; // blocked by a door - } - - // beams just check one point for PHS - if ((ent.s.renderfx & RF_BEAM) != 0) { - l = ent.clusternums[0]; - if (0 == (clientphs[l >> 3] & (1 << (l & 7)))) - continue; - } - else { - // FIXME: if an ent has a model and a sound, but isn't - // in the PVS, only the PHS, clear the model - if (ent.s.sound == 0) { - bitvector = fatpvs; //clientphs; - } - else - bitvector = fatpvs; - - if (ent.num_clusters == -1) { // too many leafs for individual check, go by headnode - if (!CM.CM_HeadnodeVisible(ent.headnode, bitvector)) - continue; - c_fullsend++; - } - else { // check individual leafs - for (i = 0; i < ent.num_clusters; i++) { - l = ent.clusternums[i]; - if ((bitvector[l >> 3] & (1 << (l & 7))) != 0) - break; - } - if (i == ent.num_clusters) - continue; // not visible - } - - if (ent.s.modelindex == 0) { // don't send sounds if they will be attenuated away - float[] delta = { 0, 0, 0 }; - float len; - - VectorSubtract(org, ent.s.origin, delta); - len = VectorLength(delta); - if (len > 400) - continue; - } - } - } - - // add it to the circular client_entities array - int ix = svs.next_client_entities % svs.num_client_entities; - state = svs.client_entities[ix]; - if (ent.s.number != e) { - Com.DPrintf("FIXING ENT.S.NUMBER!!!\n"); - ent.s.number = e; - } - - //*state = ent.s; - svs.client_entities[ix].set(ent.s); - - // don't mark players missiles as solid - if (ent.owner == client.edict) - state.solid = 0; - - svs.next_client_entities++; - frame.num_entities++; - } - } - - /* - ================== - SV_RecordDemoMessage - - Save everything in the world out without deltas. - Used for recording footage for merged or assembled demos - ================== - */ - public static void SV_RecordDemoMessage() { - int e; - edict_t ent; - entity_state_t nostate = new entity_state_t(null); - sizebuf_t buf = new sizebuf_t(); - byte buf_data[] = new byte[32768]; - int len; - - if (svs.demofile == null) - return; - - //memset (nostate, 0, sizeof(nostate)); - SZ.Init(buf, buf_data, buf_data.length); - - // write a frame message that doesn't contain a player_state_t - MSG.WriteByte(buf, svc_frame); - MSG.WriteLong(buf, sv.framenum); - - MSG.WriteByte(buf, svc_packetentities); - - e = 1; - ent = GameBase.g_edicts[e]; - - while (e < GameBase.num_edicts) { - // ignore ents without visible models unless they have an effect - if (ent.inuse - && ent.s.number != 0 - && (ent.s.modelindex != 0 || ent.s.effects != 0 || ent.s.sound != 0 || ent.s.event != 0) - && 0 == (ent.svflags & SVF_NOCLIENT)) - MSG.WriteDeltaEntity(nostate, ent.s, buf, false, true); - - e++; - ent = GameBase.g_edicts[e]; - } - - MSG.WriteShort(buf, 0); // end of packetentities - - // now add the accumulated multicast information - SZ.Write(buf, svs.demo_multicast.data, svs.demo_multicast.cursize); - SZ.Clear(svs.demo_multicast); - - // now write the entire message to the file, prefixed by the length - len = EndianHandler.swapInt(buf.cursize); - - try { - //fwrite (len, 4, 1, svs.demofile); - svs.demofile.writeInt(len); - //fwrite (buf.data, buf.cursize, 1, svs.demofile); - svs.demofile.write(buf.data, 0, buf.cursize); - } - catch (IOException e1) { - Com.Printf("Error writing demo file:" + e); - } - } -} +public class SV_ENTS { + + /* + * ============================================================================= + * + * Build a client frame structure + * + * ============================================================================= + */ + + public static byte fatpvs[] = new byte[65536 / 8]; // 32767 is MAX_MAP_LEAFS + + /* + * ============================================================================= + * + * Encode a client frame onto the network channel + * + * ============================================================================= + */ + + /* + * ============= SV_EmitPacketEntities + * + * Writes a delta update of an entity_state_t list to the message. + * ============= + */ + static void SV_EmitPacketEntities(client_frame_t from, client_frame_t to, + sizebuf_t msg) { + entity_state_t oldent = null, newent = null; + int oldindex, newindex; + int oldnum, newnum; + int from_num_entities; + int bits; + + MSG.WriteByte(msg, Defines.svc_packetentities); + + if (from == null) + from_num_entities = 0; + else + from_num_entities = from.num_entities; + + newindex = 0; + oldindex = 0; + while (newindex < to.num_entities || oldindex < from_num_entities) { + if (newindex >= to.num_entities) + newnum = 9999; + else { + newent = SV_INIT.svs.client_entities[(to.first_entity + newindex) + % SV_INIT.svs.num_client_entities]; + newnum = newent.number; + } + + if (oldindex >= from_num_entities) + oldnum = 9999; + else { + oldent = SV_INIT.svs.client_entities[(from.first_entity + oldindex) + % SV_INIT.svs.num_client_entities]; + oldnum = oldent.number; + } + + if (newnum == oldnum) { // delta update from old position + // because the force parm is false, this will not result + // in any bytes being emited if the entity has not changed at + // all + // note that players are always 'newentities', this updates + // their oldorigin always + // and prevents warping + MSG.WriteDeltaEntity(oldent, newent, msg, false, + newent.number <= SV_MAIN.maxclients.value); + oldindex++; + newindex++; + continue; + } + + if (newnum < oldnum) { // this is a new entity, send it from the + // baseline + MSG.WriteDeltaEntity(SV_INIT.sv.baselines[newnum], newent, msg, + true, true); + newindex++; + continue; + } + + if (newnum > oldnum) { // the old entity isn't present in the new + // message + bits = Defines.U_REMOVE; + if (oldnum >= 256) + bits |= Defines.U_NUMBER16 | Defines.U_MOREBITS1; + + MSG.WriteByte(msg, bits & 255); + if ((bits & 0x0000ff00) != 0) + MSG.WriteByte(msg, (bits >> 8) & 255); + + if ((bits & Defines.U_NUMBER16) != 0) + MSG.WriteShort(msg, oldnum); + else + MSG.WriteByte(msg, oldnum); + + oldindex++; + continue; + } + } + + MSG.WriteShort(msg, 0); // end of packetentities + + } + + /* + * ============= SV_WritePlayerstateToClient + * + * ============= + */ + static void SV_WritePlayerstateToClient(client_frame_t from, + client_frame_t to, sizebuf_t msg) { + int i; + int pflags; + // ptr + player_state_t ps, ops; + // mem + player_state_t dummy; + int statbits; + + ps = to.ps; + if (from == null) { + //memset (dummy, 0, sizeof(dummy)); + dummy = new player_state_t(); + ops = dummy; + } else + ops = from.ps; + + // + // determine what needs to be sent + // + pflags = 0; + + if (ps.pmove.pm_type != ops.pmove.pm_type) + pflags |= Defines.PS_M_TYPE; + + if (ps.pmove.origin[0] != ops.pmove.origin[0] + || ps.pmove.origin[1] != ops.pmove.origin[1] + || ps.pmove.origin[2] != ops.pmove.origin[2]) + pflags |= Defines.PS_M_ORIGIN; + + if (ps.pmove.velocity[0] != ops.pmove.velocity[0] + || ps.pmove.velocity[1] != ops.pmove.velocity[1] + || ps.pmove.velocity[2] != ops.pmove.velocity[2]) + pflags |= Defines.PS_M_VELOCITY; + + if (ps.pmove.pm_time != ops.pmove.pm_time) + pflags |= Defines.PS_M_TIME; + + if (ps.pmove.pm_flags != ops.pmove.pm_flags) + pflags |= Defines.PS_M_FLAGS; + + if (ps.pmove.gravity != ops.pmove.gravity) + pflags |= Defines.PS_M_GRAVITY; + + if (ps.pmove.delta_angles[0] != ops.pmove.delta_angles[0] + || ps.pmove.delta_angles[1] != ops.pmove.delta_angles[1] + || ps.pmove.delta_angles[2] != ops.pmove.delta_angles[2]) + pflags |= Defines.PS_M_DELTA_ANGLES; + + if (ps.viewoffset[0] != ops.viewoffset[0] + || ps.viewoffset[1] != ops.viewoffset[1] + || ps.viewoffset[2] != ops.viewoffset[2]) + pflags |= Defines.PS_VIEWOFFSET; + + if (ps.viewangles[0] != ops.viewangles[0] + || ps.viewangles[1] != ops.viewangles[1] + || ps.viewangles[2] != ops.viewangles[2]) + pflags |= Defines.PS_VIEWANGLES; + + if (ps.kick_angles[0] != ops.kick_angles[0] + || ps.kick_angles[1] != ops.kick_angles[1] + || ps.kick_angles[2] != ops.kick_angles[2]) + pflags |= Defines.PS_KICKANGLES; + + if (ps.blend[0] != ops.blend[0] || ps.blend[1] != ops.blend[1] + || ps.blend[2] != ops.blend[2] || ps.blend[3] != ops.blend[3]) + pflags |= Defines.PS_BLEND; + + if (ps.fov != ops.fov) + pflags |= Defines.PS_FOV; + + if (ps.rdflags != ops.rdflags) + pflags |= Defines.PS_RDFLAGS; + + if (ps.gunframe != ops.gunframe) + pflags |= Defines.PS_WEAPONFRAME; + + pflags |= Defines.PS_WEAPONINDEX; + + // + // write it + // + MSG.WriteByte(msg, Defines.svc_playerinfo); + MSG.WriteShort(msg, pflags); + + // + // write the pmove_state_t + // + if ((pflags & Defines.PS_M_TYPE) != 0) + MSG.WriteByte(msg, ps.pmove.pm_type); + + if ((pflags & Defines.PS_M_ORIGIN) != 0) { + MSG.WriteShort(msg, ps.pmove.origin[0]); + MSG.WriteShort(msg, ps.pmove.origin[1]); + MSG.WriteShort(msg, ps.pmove.origin[2]); + } + + if ((pflags & Defines.PS_M_VELOCITY) != 0) { + MSG.WriteShort(msg, ps.pmove.velocity[0]); + MSG.WriteShort(msg, ps.pmove.velocity[1]); + MSG.WriteShort(msg, ps.pmove.velocity[2]); + } + + if ((pflags & Defines.PS_M_TIME) != 0) + MSG.WriteByte(msg, ps.pmove.pm_time); + + if ((pflags & Defines.PS_M_FLAGS) != 0) + MSG.WriteByte(msg, ps.pmove.pm_flags); + + if ((pflags & Defines.PS_M_GRAVITY) != 0) + MSG.WriteShort(msg, ps.pmove.gravity); + + if ((pflags & Defines.PS_M_DELTA_ANGLES) != 0) { + MSG.WriteShort(msg, ps.pmove.delta_angles[0]); + MSG.WriteShort(msg, ps.pmove.delta_angles[1]); + MSG.WriteShort(msg, ps.pmove.delta_angles[2]); + } + + // + // write the rest of the player_state_t + // + if ((pflags & Defines.PS_VIEWOFFSET) != 0) { + MSG.WriteChar(msg, ps.viewoffset[0] * 4); + MSG.WriteChar(msg, ps.viewoffset[1] * 4); + MSG.WriteChar(msg, ps.viewoffset[2] * 4); + } + + if ((pflags & Defines.PS_VIEWANGLES) != 0) { + MSG.WriteAngle16(msg, ps.viewangles[0]); + MSG.WriteAngle16(msg, ps.viewangles[1]); + MSG.WriteAngle16(msg, ps.viewangles[2]); + } + + if ((pflags & Defines.PS_KICKANGLES) != 0) { + MSG.WriteChar(msg, ps.kick_angles[0] * 4); + MSG.WriteChar(msg, ps.kick_angles[1] * 4); + MSG.WriteChar(msg, ps.kick_angles[2] * 4); + } + + if ((pflags & Defines.PS_WEAPONINDEX) != 0) { + MSG.WriteByte(msg, ps.gunindex); + } + + if ((pflags & Defines.PS_WEAPONFRAME) != 0) { + MSG.WriteByte(msg, ps.gunframe); + MSG.WriteChar(msg, ps.gunoffset[0] * 4); + MSG.WriteChar(msg, ps.gunoffset[1] * 4); + MSG.WriteChar(msg, ps.gunoffset[2] * 4); + MSG.WriteChar(msg, ps.gunangles[0] * 4); + MSG.WriteChar(msg, ps.gunangles[1] * 4); + MSG.WriteChar(msg, ps.gunangles[2] * 4); + } + + if ((pflags & Defines.PS_BLEND) != 0) { + MSG.WriteByte(msg, ps.blend[0] * 255); + MSG.WriteByte(msg, ps.blend[1] * 255); + MSG.WriteByte(msg, ps.blend[2] * 255); + MSG.WriteByte(msg, ps.blend[3] * 255); + } + if ((pflags & Defines.PS_FOV) != 0) + MSG.WriteByte(msg, ps.fov); + if ((pflags & Defines.PS_RDFLAGS) != 0) + MSG.WriteByte(msg, ps.rdflags); + + // send stats + statbits = 0; + for (i = 0; i < Defines.MAX_STATS; i++) + if (ps.stats[i] != ops.stats[i]) + statbits |= 1 << i; + MSG.WriteLong(msg, statbits); + for (i = 0; i < Defines.MAX_STATS; i++) + if ((statbits & (1 << i)) != 0) + MSG.WriteShort(msg, ps.stats[i]); + } + + /* + * ================== SV_WriteFrameToClient ================== + */ + public static void SV_WriteFrameToClient(client_t client, sizebuf_t msg) { + //ptr + client_frame_t frame, oldframe; + int lastframe; + + //Com.Printf ("%i . %i\n", new + // Vargs().add(client.lastframe).add(sv.framenum)); + // this is the frame we are creating + frame = client.frames[SV_INIT.sv.framenum & Defines.UPDATE_MASK]; + if (client.lastframe <= 0) { // client is asking for a retransmit + oldframe = null; + lastframe = -1; + } else if (SV_INIT.sv.framenum - client.lastframe >= (Defines.UPDATE_BACKUP - 3)) { + // client hasn't gotten a good message through in a long time + // Com_Printf ("%s: Delta request from out-of-date packet.\n", + // client.name); + oldframe = null; + lastframe = -1; + } else { // we have a valid message to delta from + oldframe = client.frames[client.lastframe & Defines.UPDATE_MASK]; + lastframe = client.lastframe; + } + + MSG.WriteByte(msg, Defines.svc_frame); + MSG.WriteLong(msg, SV_INIT.sv.framenum); + MSG.WriteLong(msg, lastframe); // what we are delta'ing from + MSG.WriteByte(msg, client.surpressCount); // rate dropped packets + client.surpressCount = 0; + + // send over the areabits + MSG.WriteByte(msg, frame.areabytes); + SZ.Write(msg, frame.areabits, frame.areabytes); + + // delta encode the playerstate + SV_WritePlayerstateToClient(oldframe, frame, msg); + + // delta encode the entities + SV_EmitPacketEntities(oldframe, frame, msg); + } + + /* + * ============ SV_FatPVS + * + * The client will interpolate the view position, so we can't use a single + * PVS point =========== + */ + public static void SV_FatPVS(float[] org) { + int leafs[] = new int[64]; + int i, j, count; + int longs; + byte src[]; + float[] mins = { 0, 0, 0 }, maxs = { 0, 0, 0 }; + + for (i = 0; i < 3; i++) { + mins[i] = org[i] - 8; + maxs[i] = org[i] + 8; + } + + count = CM.CM_BoxLeafnums(mins, maxs, leafs, 64, null); + + if (count < 1) + Com.Error(Defines.ERR_FATAL, "SV_FatPVS: count < 1"); + + longs = (CM.CM_NumClusters() + 31) >> 5; + + // convert leafs to clusters + for (i = 0; i < count; i++) + leafs[i] = CM.CM_LeafCluster(leafs[i]); + + System.arraycopy(CM.CM_ClusterPVS(leafs[0]), 0, SV_ENTS.fatpvs, 0, + longs << 2); + // or in all the other leaf bits + for (i = 1; i < count; i++) { + for (j = 0; j < i; j++) + if (leafs[i] == leafs[j]) + break; + if (j != i) + continue; // already have the cluster we want + + src = CM.CM_ClusterPVS(leafs[i]); + + //for (j=0 ; j<longs ; j++) + // ((long *)fatpvs)[j] |= ((long *)src)[j]; + int k = 0; + for (j = 0; j < longs; j++) { + SV_ENTS.fatpvs[k] |= src[k++]; + SV_ENTS.fatpvs[k] |= src[k++]; + SV_ENTS.fatpvs[k] |= src[k++]; + SV_ENTS.fatpvs[k] |= src[k++]; + } + } + } + + /* + * ============= SV_BuildClientFrame + * + * Decides which entities are going to be visible to the client, and copies + * off the playerstat and areabits. ============= + */ + public static void SV_BuildClientFrame(client_t client) { + int e, i; + float[] org = { 0, 0, 0 }; + edict_t ent; + edict_t clent; + client_frame_t frame; + entity_state_t state; + int l; + int clientarea, clientcluster; + int leafnum; + int c_fullsend; + byte clientphs[]; + byte bitvector[]; + + clent = client.edict; + if (clent.client == null) + return; // not in game yet + + // this is the frame we are creating + frame = client.frames[SV_INIT.sv.framenum & Defines.UPDATE_MASK]; + + frame.senttime = SV_INIT.svs.realtime; // save it for ping calc later + + // find the client's PVS + for (i = 0; i < 3; i++) + org[i] = clent.client.ps.pmove.origin[i] * 0.125f + + clent.client.ps.viewoffset[i]; + + leafnum = CM.CM_PointLeafnum(org); + clientarea = CM.CM_LeafArea(leafnum); + clientcluster = CM.CM_LeafCluster(leafnum); + + // calculate the visible areas + frame.areabytes = CM.CM_WriteAreaBits(frame.areabits, clientarea); + + // grab the current player_state_t + frame.ps.set(clent.client.ps); + + SV_FatPVS(org); + clientphs = CM.CM_ClusterPHS(clientcluster); + + // build up the list of visible entities + frame.num_entities = 0; + frame.first_entity = SV_INIT.svs.next_client_entities; + + c_fullsend = 0; + + for (e = 1; e < GameBase.num_edicts; e++) { + ent = GameBase.g_edicts[e]; + + // ignore ents without visible models + if ((ent.svflags & Defines.SVF_NOCLIENT) != 0) + continue; + + // ignore ents without visible models unless they have an effect + if (0 == ent.s.modelindex && 0 == ent.s.effects && 0 == ent.s.sound + && 0 == ent.s.event) + continue; + + // ignore if not touching a PV leaf + if (ent != clent) { + // check area + if (!CM.CM_AreasConnected(clientarea, ent.areanum)) { // doors + // can + // legally + // straddle + // two + // areas, + // so + // we may need to check another one + if (0 == ent.areanum2 + || !CM.CM_AreasConnected(clientarea, ent.areanum2)) + continue; // blocked by a door + } + + // beams just check one point for PHS + if ((ent.s.renderfx & Defines.RF_BEAM) != 0) { + l = ent.clusternums[0]; + if (0 == (clientphs[l >> 3] & (1 << (l & 7)))) + continue; + } else { + // FIXME: if an ent has a model and a sound, but isn't + // in the PVS, only the PHS, clear the model + if (ent.s.sound == 0) { + bitvector = SV_ENTS.fatpvs; //clientphs; + } else + bitvector = SV_ENTS.fatpvs; + + if (ent.num_clusters == -1) { // too many leafs for + // individual check, go by + // headnode + if (!CM.CM_HeadnodeVisible(ent.headnode, bitvector)) + continue; + c_fullsend++; + } else { // check individual leafs + for (i = 0; i < ent.num_clusters; i++) { + l = ent.clusternums[i]; + if ((bitvector[l >> 3] & (1 << (l & 7))) != 0) + break; + } + if (i == ent.num_clusters) + continue; // not visible + } + + if (ent.s.modelindex == 0) { // don't send sounds if they + // will be attenuated away + float[] delta = { 0, 0, 0 }; + float len; + + Math3D.VectorSubtract(org, ent.s.origin, delta); + len = Math3D.VectorLength(delta); + if (len > 400) + continue; + } + } + } + + // add it to the circular client_entities array + int ix = SV_INIT.svs.next_client_entities + % SV_INIT.svs.num_client_entities; + state = SV_INIT.svs.client_entities[ix]; + if (ent.s.number != e) { + Com.DPrintf("FIXING ENT.S.NUMBER!!!\n"); + ent.s.number = e; + } + + //*state = ent.s; + SV_INIT.svs.client_entities[ix].set(ent.s); + + // don't mark players missiles as solid + if (ent.owner == client.edict) + state.solid = 0; + + SV_INIT.svs.next_client_entities++; + frame.num_entities++; + } + } + + /* + * ================== SV_RecordDemoMessage + * + * Save everything in the world out without deltas. Used for recording + * footage for merged or assembled demos ================== + */ + public static void SV_RecordDemoMessage() { + int e; + edict_t ent; + entity_state_t nostate = new entity_state_t(null); + sizebuf_t buf = new sizebuf_t(); + byte buf_data[] = new byte[32768]; + int len; + + if (SV_INIT.svs.demofile == null) + return; + + //memset (nostate, 0, sizeof(nostate)); + SZ.Init(buf, buf_data, buf_data.length); + + // write a frame message that doesn't contain a player_state_t + MSG.WriteByte(buf, Defines.svc_frame); + MSG.WriteLong(buf, SV_INIT.sv.framenum); + + MSG.WriteByte(buf, Defines.svc_packetentities); + + e = 1; + ent = GameBase.g_edicts[e]; + + while (e < GameBase.num_edicts) { + // ignore ents without visible models unless they have an effect + if (ent.inuse + && ent.s.number != 0 + && (ent.s.modelindex != 0 || ent.s.effects != 0 + || ent.s.sound != 0 || ent.s.event != 0) + && 0 == (ent.svflags & Defines.SVF_NOCLIENT)) + MSG.WriteDeltaEntity(nostate, ent.s, buf, false, true); + + e++; + ent = GameBase.g_edicts[e]; + } + + MSG.WriteShort(buf, 0); // end of packetentities + + // now add the accumulated multicast information + SZ.Write(buf, SV_INIT.svs.demo_multicast.data, + SV_INIT.svs.demo_multicast.cursize); + SZ.Clear(SV_INIT.svs.demo_multicast); + + // now write the entire message to the file, prefixed by the length + len = EndianHandler.swapInt(buf.cursize); + + try { + //fwrite (len, 4, 1, svs.demofile); + SV_INIT.svs.demofile.writeInt(len); + //fwrite (buf.data, buf.cursize, 1, svs.demofile); + SV_INIT.svs.demofile.write(buf.data, 0, buf.cursize); + } catch (IOException e1) { + Com.Printf("Error writing demo file:" + e); + } + } +}
\ No newline at end of file |