aboutsummaryrefslogtreecommitdiffstats
path: root/src/jake2/server
diff options
context:
space:
mode:
authorRene Stoeckel <[email protected]>2004-09-22 19:22:16 +0000
committerRene Stoeckel <[email protected]>2004-09-22 19:22:16 +0000
commitc4fcffe436fbfb5b0f3b7be2e5ee103ec74932f7 (patch)
tree7c9439ab1d9f5a4fd61bd57c755069007b23e0b6 /src/jake2/server
parentbcb4ac6eefb425d5b0a90009da506361d5739e75 (diff)
major refactoring in game, server and client package
Diffstat (limited to 'src/jake2/server')
-rw-r--r--src/jake2/server/SV.java2298
-rw-r--r--src/jake2/server/SV_CCMDS.java390
-rw-r--r--src/jake2/server/SV_ENTS.java1203
-rw-r--r--src/jake2/server/SV_GAME.java617
-rw-r--r--src/jake2/server/SV_INIT.java962
-rw-r--r--src/jake2/server/SV_MAIN.java2034
-rw-r--r--src/jake2/server/SV_SEND.java266
-rw-r--r--src/jake2/server/SV_USER.java1408
-rw-r--r--src/jake2/server/SV_WORLD.java1198
-rw-r--r--src/jake2/server/server_static_t.java98
-rw-r--r--src/jake2/server/server_t.java124
11 files changed, 5180 insertions, 5418 deletions
diff --git a/src/jake2/server/SV.java b/src/jake2/server/SV.java
index 701f767..a046ef4 100644
--- a/src/jake2/server/SV.java
+++ b/src/jake2/server/SV.java
@@ -2,32 +2,36 @@
* SV.java
* Copyright (C) 2003
*
- * $Id: SV.java,v 1.9 2004-08-29 21:39:25 hzi Exp $
+ * $Id: SV.java,v 1.10 2004-09-22 19:22:12 salomo Exp $
*/
/*
-Copyright (C) 1997-2001 Id Software, Inc.
+ 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 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.
+ 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.
+ 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.
+ 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.
-*/
+ */
package jake2.server;
import jake2.Defines;
+import jake2.Globals;
import jake2.client.M;
-import jake2.game.*;
+import jake2.game.GameBase;
+import jake2.game.edict_t;
+import jake2.game.pushed_t;
+import jake2.game.trace_t;
import jake2.qcommon.Com;
import jake2.util.Lib;
import jake2.util.Math3D;
@@ -35,1175 +39,1095 @@ import jake2.util.Math3D;
/**
* SV
*/
-public final class SV
-{
-
- //file_io
- //=====================================================================
- //g_phys
-
- ///////////////////////////////////////
- public static edict_t[] SV_TestEntityPosition(edict_t ent)
- {
- trace_t trace;
- int mask;
-
- if (ent.clipmask != 0)
- mask= ent.clipmask;
- else
- mask= Defines.MASK_SOLID;
-
- trace= GameBase.gi.trace(ent.s.origin, ent.mins, ent.maxs, ent.s.origin, ent, mask);
-
- if (trace.startsolid)
- return GameBase.g_edicts;
-
- return null;
- }
-
- ///////////////////////////////////////
- public static void SV_CheckVelocity(edict_t ent)
- {
- int i;
-
- //
- // bound velocity
- //
- for (i= 0; i < 3; i++)
- {
- if (ent.velocity[i] > GameBase.sv_maxvelocity.value)
- ent.velocity[i]= GameBase.sv_maxvelocity.value;
- else if (ent.velocity[i] < -GameBase.sv_maxvelocity.value)
- ent.velocity[i]= -GameBase.sv_maxvelocity.value;
- }
- }
-
- /**
- * Runs thinking code for this frame if necessary.
- */
- public static boolean SV_RunThink(edict_t ent)
- {
- float thinktime;
-
- thinktime= ent.nextthink;
- if (thinktime <= 0)
- return true;
- if (thinktime > GameBase.level.time + 0.001)
- return true;
-
- ent.nextthink= 0;
-
- if (ent.think == null)
- Com.Error(Defines.ERR_FATAL, "NULL ent.think");
-
- ent.think.think(ent);
-
- return false;
- }
-
- /**
- * Two entities have touched, so run their touch functions.
- */
- public static void SV_Impact(edict_t e1, trace_t trace)
- {
- edict_t e2;
-
- e2= trace.ent;
-
- if (e1.touch != null && e1.solid != Defines.SOLID_NOT)
- e1.touch.touch(e1, e2, trace.plane, trace.surface);
-
- if (e2.touch != null && e2.solid != Defines.SOLID_NOT)
- e2.touch.touch(e2, e1, GameBase.dummyplane, null);
- }
-
- public static int SV_FlyMove(edict_t ent, float time, int mask)
- {
- edict_t hit;
- int bumpcount, numbumps;
- float[] dir= { 0.0f, 0.0f, 0.0f };
- float d;
- int numplanes;
- float[][] planes= new float[GameBase.MAX_CLIP_PLANES][3];
- float[] primal_velocity= { 0.0f, 0.0f, 0.0f };
- float[] original_velocity= { 0.0f, 0.0f, 0.0f };
- float[] new_velocity= { 0.0f, 0.0f, 0.0f };
- int i, j;
- trace_t trace;
- float[] end= { 0.0f, 0.0f, 0.0f };
- float time_left;
- int blocked;
-
- numbumps= 4;
-
- blocked= 0;
- Math3D.VectorCopy(ent.velocity, original_velocity);
- Math3D.VectorCopy(ent.velocity, primal_velocity);
- numplanes= 0;
-
- time_left= time;
-
- ent.groundentity= null;
- for (bumpcount= 0; bumpcount < numbumps; bumpcount++)
- {
- for (i= 0; i < 3; i++)
- end[i]= ent.s.origin[i] + time_left * ent.velocity[i];
-
- trace= GameBase.gi.trace(ent.s.origin, ent.mins, ent.maxs, end, ent, mask);
-
- if (trace.allsolid)
- { // entity is trapped in another solid
- Math3D.VectorCopy(GameBase.vec3_origin, ent.velocity);
- return 3;
- }
-
- if (trace.fraction > 0)
- { // actually covered some distance
- Math3D.VectorCopy(trace.endpos, ent.s.origin);
- Math3D.VectorCopy(ent.velocity, original_velocity);
- numplanes= 0;
- }
-
- if (trace.fraction == 1)
- break; // moved the entire distance
-
- hit= trace.ent;
-
- if (trace.plane.normal[2] > 0.7)
- {
- blocked |= 1; // floor
- if (hit.solid == Defines.SOLID_BSP)
- {
- ent.groundentity= hit;
- ent.groundentity_linkcount= hit.linkcount;
- }
- }
- if (trace.plane.normal[2] == 0.0f)
- {
- blocked |= 2; // step
- }
-
- //
- // run the impact function
- //
- SV_Impact(ent, trace);
- if (!ent.inuse)
- break; // removed by the impact function
-
- time_left -= time_left * trace.fraction;
-
- // cliped to another plane
- if (numplanes >= GameBase.MAX_CLIP_PLANES)
- { // this shouldn't really happen
- Math3D.VectorCopy(GameBase.vec3_origin, ent.velocity);
- return 3;
- }
-
- Math3D.VectorCopy(trace.plane.normal, planes[numplanes]);
- numplanes++;
-
- //
- // modify original_velocity so it parallels all of the clip planes
- //
- for (i= 0; i < numplanes; i++)
- {
- GameBase.ClipVelocity(original_velocity, planes[i], new_velocity, 1);
-
- for (j= 0; j < numplanes; j++)
- if ((j != i) && Math3D.VectorCompare(planes[i], planes[j]) == 0.0f)
- {
- if (Math3D.DotProduct(new_velocity, planes[j]) < 0)
- break; // not ok
- }
- if (j == numplanes)
- break;
- }
-
- if (i != numplanes)
- { // go along this plane
- Math3D.VectorCopy(new_velocity, ent.velocity);
- }
- else
- { // go along the crease
- if (numplanes != 2)
- {
- // gi.dprintf ("clip velocity, numplanes == %i\n",numplanes);
- Math3D.VectorCopy(GameBase.vec3_origin, ent.velocity);
- return 7;
- }
- Math3D.CrossProduct(planes[0], planes[1], dir);
- d= Math3D.DotProduct(dir, ent.velocity);
- Math3D.VectorScale(dir, d, ent.velocity);
- }
-
- //
- // if original velocity is against the original velocity, stop dead
- // to avoid tiny occilations in sloping corners
- //
- if (Math3D.DotProduct(ent.velocity, primal_velocity) <= 0)
- {
- Math3D.VectorCopy(GameBase.vec3_origin, ent.velocity);
- return blocked;
- }
- }
-
- return blocked;
- }
-
- /*
- ============
- SV_AddGravity
-
- ============
- */
- public static void SV_AddGravity(edict_t ent)
- {
- ent.velocity[2] -= ent.gravity * GameBase.sv_gravity.value * Defines.FRAMETIME;
- }
-
- /**
- * Does not change the entities velocity at all
- */
- public static trace_t SV_PushEntity(edict_t ent, float[] push)
- {
- trace_t trace;
- float[] start= { 0, 0, 0 };
- float[] end= { 0, 0, 0 };
- int mask;
-
- Math3D.VectorCopy(ent.s.origin, start);
- Math3D.VectorAdd(start, push, end);
-
- // FIXME: test this
- // a goto statement was replaced.
- boolean retry= false;
-
- do
- {
- if (ent.clipmask != 0)
- mask= ent.clipmask;
- else
- mask= Defines.MASK_SOLID;
-
- trace= GameBase.gi.trace(start, ent.mins, ent.maxs, end, ent, mask);
-
- Math3D.VectorCopy(trace.endpos, ent.s.origin);
- GameBase.gi.linkentity(ent);
-
- retry= false;
- if (trace.fraction != 1.0)
- {
- SV_Impact(ent, trace);
-
- // if the pushed entity went away and the pusher is still there
- if (!trace.ent.inuse && ent.inuse)
- {
- // move the pusher back and try again
- Math3D.VectorCopy(start, ent.s.origin);
- GameBase.gi.linkentity(ent);
- //goto retry;
- retry= true;
- }
- }
- }
- while (retry);
-
- if (ent.inuse)
- GameBase.G_TouchTriggers(ent);
-
- return trace;
- }
-
- /*
- ============
- SV_Push
-
- Objects need to be moved back on a failed push,
- otherwise riders would continue to slide.
- ============
- */
- public static boolean SV_Push(edict_t pusher, float[] move, float[] amove)
- {
- int i, e;
- edict_t check, block[];
- float[] mins= { 0, 0, 0 };
- float[] maxs= { 0, 0, 0 };
- pushed_t p;
- float[] org= { 0, 0, 0 };
- float[] org2= { 0, 0, 0 };
- float[] move2= { 0, 0, 0 };
- float[] forward= { 0, 0, 0 };
- float[] right= { 0, 0, 0 };
- float[] up= { 0, 0, 0 };
-
- // clamp the move to 1/8 units, so the position will
- // be accurate for client side prediction
- for (i= 0; i < 3; i++)
- {
- float temp;
- temp= move[i] * 8.0f;
- if (temp > 0.0)
- temp += 0.5;
- else
- temp -= 0.5;
- move[i]= 0.125f * (int) temp;
- }
-
- // find the bounding box
- for (i= 0; i < 3; i++)
- {
- mins[i]= pusher.absmin[i] + move[i];
- maxs[i]= pusher.absmax[i] + move[i];
- }
-
- // we need this for pushing things later
- Math3D.VectorSubtract(GameBase.vec3_origin, amove, org);
- Math3D.AngleVectors(org, forward, right, up);
-
- // save the pusher's original position
- GameBase.pushed[GameBase.pushed_p].ent= pusher;
- Math3D.VectorCopy(pusher.s.origin, GameBase.pushed[GameBase.pushed_p].origin);
- Math3D.VectorCopy(pusher.s.angles, GameBase.pushed[GameBase.pushed_p].angles);
-
- if (pusher.client != null)
- GameBase.pushed[GameBase.pushed_p].deltayaw= pusher.client.ps.pmove.delta_angles[Defines.YAW];
-
- GameBase.pushed_p++;
-
- // move the pusher to it's final position
- Math3D.VectorAdd(pusher.s.origin, move, pusher.s.origin);
- Math3D.VectorAdd(pusher.s.angles, amove, pusher.s.angles);
- GameBase.gi.linkentity(pusher);
-
- // see if any solid entities are inside the final position
-
- //check= g_edicts + 1;
- for (e= 1; e < GameBase.num_edicts; e++)
- {
- check= GameBase.g_edicts[e];
- if (!check.inuse)
- continue;
- if (check.movetype == Defines.MOVETYPE_PUSH
- || check.movetype == Defines.MOVETYPE_STOP
- || check.movetype == Defines.MOVETYPE_NONE
- || check.movetype == Defines.MOVETYPE_NOCLIP)
- continue;
-
- if (check.area.prev == null)
- continue; // not linked in anywhere
-
- // if the entity is standing on the pusher, it will definitely be moved
- if (check.groundentity != pusher)
- {
- // see if the ent needs to be tested
- if (check.absmin[0] >= maxs[0]
- || check.absmin[1] >= maxs[1]
- || check.absmin[2] >= maxs[2]
- || check.absmax[0] <= mins[0]
- || check.absmax[1] <= mins[1]
- || check.absmax[2] <= mins[2])
- continue;
-
- // see if the ent's bbox is inside the pusher's final position
- if (SV_TestEntityPosition(check) == null)
- continue;
- }
-
- if ((pusher.movetype == Defines.MOVETYPE_PUSH) || (check.groundentity == pusher))
- {
- // move this entity
- GameBase.pushed[GameBase.pushed_p].ent= check;
- Math3D.VectorCopy(check.s.origin, GameBase.pushed[GameBase.pushed_p].origin);
- Math3D.VectorCopy(check.s.angles, GameBase.pushed[GameBase.pushed_p].angles);
- GameBase.pushed_p++;
-
- // try moving the contacted entity
- Math3D.VectorAdd(check.s.origin, move, check.s.origin);
- if (check.client != null)
- { // FIXME: doesn't rotate monsters?
- check.client.ps.pmove.delta_angles[Defines.YAW] += amove[Defines.YAW];
- }
-
- // figure movement due to the pusher's amove
- Math3D.VectorSubtract(check.s.origin, pusher.s.origin, org);
- org2[0]= Math3D.DotProduct(org, forward);
- org2[1]= -Math3D.DotProduct(org, right);
- org2[2]= Math3D.DotProduct(org, up);
- Math3D.VectorSubtract(org2, org, move2);
- Math3D.VectorAdd(check.s.origin, move2, check.s.origin);
-
- // may have pushed them off an edge
- if (check.groundentity != pusher)
- check.groundentity= null;
-
- block= SV_TestEntityPosition(check);
- if (block == null)
- { // pushed ok
- GameBase.gi.linkentity(check);
- // impact?
- continue;
- }
-
- // if it is ok to leave in the old position, do it
- // this is only relevent for riding entities, not pushed
- // FIXME: this doesn't acount for rotation
- Math3D.VectorSubtract(check.s.origin, move, check.s.origin);
- block= SV_TestEntityPosition(check);
-
- if (block == null)
- {
- GameBase.pushed_p--;
- continue;
- }
- }
-
- // save off the obstacle so we can call the block function
- GameBase.obstacle= check;
-
- // move back any entities we already moved
- // go backwards, so if the same entity was pushed
- // twice, it goes back to the original position
- for (int ip= GameBase.pushed_p - 1; ip >= 0; ip--)
- {
- p= GameBase.pushed[ip];
- Math3D.VectorCopy(p.origin, p.ent.s.origin);
- Math3D.VectorCopy(p.angles, p.ent.s.angles);
- if (p.ent.client != null)
- {
- p.ent.client.ps.pmove.delta_angles[Defines.YAW]= (short) p.deltayaw;
- }
- GameBase.gi.linkentity(p.ent);
- }
- return false;
- }
-
- // FIXME: is there a better way to handle this?
- // see if anything we moved has touched a trigger
- for (int ip= GameBase.pushed_p - 1; ip >= 0; ip--)
- GameBase.G_TouchTriggers(GameBase.pushed[ip].ent);
-
- return true;
- }
-
- /*
- ================
- SV_Physics_Pusher
-
- Bmodel objects don't interact with each other, but
- push all box objects
- ================
- */
- public static void SV_Physics_Pusher(edict_t ent)
- {
- float[] move= { 0, 0, 0 };
- float[] amove= { 0, 0, 0 };
- edict_t part, mv;
-
- // if not a team captain, so movement will be handled elsewhere
- if ((ent.flags & Defines.FL_TEAMSLAVE) != 0)
- return;
-
- // make sure all team slaves can move before commiting
- // any moves or calling any think functions
- // if the move is blocked, all moved objects will be backed out
- // retry:
- GameBase.pushed_p= 0;
- for (part= ent; part != null; part= part.teamchain)
- {
- if (part.velocity[0] != 0
- || part.velocity[1] != 0
- || part.velocity[2] != 0
- || part.avelocity[0] != 0
- || part.avelocity[1] != 0
- || part.avelocity[2] != 0)
- { // object is moving
- Math3D.VectorScale(part.velocity, Defines.FRAMETIME, move);
- Math3D.VectorScale(part.avelocity, Defines.FRAMETIME, amove);
-
- if (!SV_Push(part, move, amove))
- break; // move was blocked
- }
- }
- if (GameBase.pushed_p > Defines.MAX_EDICTS)
-
- SV_GAME.PF_error(Defines.ERR_FATAL, "pushed_p > &pushed[MAX_EDICTS], memory corrupted");
-
- if (part != null) {
- // the move failed, bump all nextthink times and back out moves
- for (mv= ent; mv != null; mv= mv.teamchain)
- {
- if (mv.nextthink > 0)
- mv.nextthink += Defines.FRAMETIME;
- }
-
- // if the pusher has a "blocked" function, call it
- // otherwise, just stay in place until the obstacle is gone
- if (part.blocked != null)
- part.blocked.blocked(part, GameBase.obstacle);
- }
- else
- { // the move succeeded, so call all think functions
- for (part= ent; part != null; part= part.teamchain)
- {
- SV_RunThink(part);
- }
- }
- }
-
- // ==================================================================
-
- /*
- =============
- SV_Physics_None
-
- Non moving objects can only think
- =============
- */
- public static void SV_Physics_None(edict_t ent)
- {
- // regular thinking
- SV_RunThink(ent);
- }
-
- /*
- =============
- SV_Physics_Noclip
-
- A moving object that doesn't obey physics
- =============
- */
- public static void SV_Physics_Noclip(edict_t ent)
- {
- // regular thinking
- if (!SV_RunThink(ent))
- return;
-
- Math3D.VectorMA(ent.s.angles, Defines.FRAMETIME, ent.avelocity, ent.s.angles);
- Math3D.VectorMA(ent.s.origin, Defines.FRAMETIME, ent.velocity, ent.s.origin);
-
- GameBase.gi.linkentity(ent);
- }
-
- /*
- ==============================================================================
-
- TOSS / BOUNCE
-
- ==============================================================================
- */
-
- /*
- =============
- SV_Physics_Toss
-
- Toss, bounce, and fly movement. When onground, do nothing.
- =============
- */
- public static void SV_Physics_Toss(edict_t ent)
- {
-
- trace_t trace;
- float[] move= { 0, 0, 0 };
- float backoff;
- edict_t slave;
- boolean wasinwater;
- boolean isinwater;
- float[] old_origin= { 0, 0, 0 };
-
- // regular thinking
- SV_RunThink(ent);
-
- // if not a team captain, so movement will be handled elsewhere
- if ((ent.flags & Defines.FL_TEAMSLAVE) != 0)
- return;
-
- if (ent.velocity[2] > 0)
- ent.groundentity= null;
-
- // check for the groundentity going away
- if (ent.groundentity != null)
- if (!ent.groundentity.inuse)
- ent.groundentity= null;
-
- // if onground, return without moving
- if (ent.groundentity != null)
- return;
-
- Math3D.VectorCopy(ent.s.origin, old_origin);
-
- SV_CheckVelocity(ent);
-
- // add gravity
- if (ent.movetype != Defines.MOVETYPE_FLY && ent.movetype != Defines.MOVETYPE_FLYMISSILE)
- SV_AddGravity(ent);
-
- // move angles
- Math3D.VectorMA(ent.s.angles, Defines.FRAMETIME, ent.avelocity, ent.s.angles);
-
- // move origin
- Math3D.VectorScale(ent.velocity, Defines.FRAMETIME, move);
- trace= SV_PushEntity(ent, move);
- if (!ent.inuse)
- return;
-
- if (trace.fraction < 1)
- {
- if (ent.movetype == Defines.MOVETYPE_BOUNCE)
- backoff= 1.5f;
- else
- backoff= 1;
-
- GameBase.ClipVelocity(ent.velocity, trace.plane.normal, ent.velocity, backoff);
-
- // stop if on ground
- if (trace.plane.normal[2] > 0.7)
- {
- if (ent.velocity[2] < 60 || ent.movetype != Defines.MOVETYPE_BOUNCE)
- {
- ent.groundentity= trace.ent;
- ent.groundentity_linkcount= trace.ent.linkcount;
- Math3D.VectorCopy(GameBase.vec3_origin, ent.velocity);
- Math3D.VectorCopy(GameBase.vec3_origin, ent.avelocity);
- }
- }
-
- // if (ent.touch)
- // ent.touch (ent, trace.ent, &trace.plane, trace.surface);
- }
-
- // check for water transition
- wasinwater= (ent.watertype & Defines.MASK_WATER) != 0;
- ent.watertype= GameBase.gi.pointcontents.pointcontents(ent.s.origin);
- isinwater= (ent.watertype & Defines.MASK_WATER) != 0;
-
- if (isinwater)
- ent.waterlevel= 1;
- else
- ent.waterlevel= 0;
-
- if (!wasinwater && isinwater)
- GameBase.gi.positioned_sound(old_origin, ent, Defines.CHAN_AUTO, GameBase.gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
- else if (wasinwater && !isinwater)
- GameBase.gi.positioned_sound(ent.s.origin, ent, Defines.CHAN_AUTO, GameBase.gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
-
- // move teamslaves
- for (slave= ent.teamchain; slave != null; slave= slave.teamchain)
- {
- Math3D.VectorCopy(ent.s.origin, slave.s.origin);
- GameBase.gi.linkentity(slave);
- }
- }
-
- /*
- ===============================================================================
-
- STEPPING MOVEMENT
-
- ===============================================================================
- */
-
- /*
- =============
- SV_Physics_Step
-
- Monsters freefall when they don't have a ground entity, otherwise
- all movement is done with discrete steps.
-
- This is also used for objects that have become still on the ground, but
- will fall if the floor is pulled out from under them.
- FIXME: is this true?
- =============
- */
-
- // FIXME: hacked in for E3 demo
-
- public static void SV_AddRotationalFriction(edict_t ent)
- {
- int n;
- float adjustment;
-
- Math3D.VectorMA(ent.s.angles, Defines.FRAMETIME, ent.avelocity, ent.s.angles);
- adjustment= Defines.FRAMETIME * Defines.sv_stopspeed * Defines.sv_friction;
- for (n= 0; n < 3; n++)
- {
- if (ent.avelocity[n] > 0)
- {
- ent.avelocity[n] -= adjustment;
- if (ent.avelocity[n] < 0)
- ent.avelocity[n]= 0;
- }
- else
- {
- ent.avelocity[n] += adjustment;
- if (ent.avelocity[n] > 0)
- ent.avelocity[n]= 0;
- }
- }
- }
-
- public static void SV_Physics_Step(edict_t ent)
- {
- boolean wasonground;
- boolean hitsound= false;
- float vel[];
- float speed, newspeed, control;
- float friction;
- edict_t groundentity;
- int mask;
-
- // airborn monsters should always check for ground
- if (ent.groundentity == null)
- M.M_CheckGround(ent);
-
- groundentity= ent.groundentity;
-
- SV_CheckVelocity(ent);
-
- if (groundentity != null)
- wasonground= true;
- else
- wasonground= false;
-
- if (ent.avelocity[0] != 0 || ent.avelocity[1] != 0 || ent.avelocity[2] != 0)
- SV_AddRotationalFriction(ent);
-
- // add gravity except:
- // flying monsters
- // swimming monsters who are in the water
- if (!wasonground)
- if (0 == (ent.flags & Defines.FL_FLY))
- if (!((ent.flags & Defines.FL_SWIM) != 0 && (ent.waterlevel > 2)))
- {
- if (ent.velocity[2] < GameBase.sv_gravity.value * -0.1)
- hitsound= true;
- if (ent.waterlevel == 0)
- SV_AddGravity(ent);
- }
-
- // friction for flying monsters that have been given vertical velocity
- if ((ent.flags & Defines.FL_FLY) != 0 && (ent.velocity[2] != 0))
- {
- speed= Math.abs(ent.velocity[2]);
- control= speed < Defines.sv_stopspeed ? Defines.sv_stopspeed : speed;
- friction= Defines.sv_friction / 3;
- newspeed= speed - (Defines.FRAMETIME * control * friction);
- if (newspeed < 0)
- newspeed= 0;
- newspeed /= speed;
- ent.velocity[2] *= newspeed;
- }
-
- // friction for flying monsters that have been given vertical velocity
- if ((ent.flags & Defines.FL_SWIM) != 0 && (ent.velocity[2] != 0))
- {
- speed= Math.abs(ent.velocity[2]);
- control= speed < Defines.sv_stopspeed ? Defines.sv_stopspeed : speed;
- newspeed= speed - (Defines.FRAMETIME * control * Defines.sv_waterfriction * ent.waterlevel);
- if (newspeed < 0)
- newspeed= 0;
- newspeed /= speed;
- ent.velocity[2] *= newspeed;
- }
-
- if (ent.velocity[2] != 0 || ent.velocity[1] != 0 || ent.velocity[0] != 0)
- {
- // apply friction
- // let dead monsters who aren't completely onground slide
- if ((wasonground) || 0 != (ent.flags & (Defines.FL_SWIM | Defines.FL_FLY)))
- if (!(ent.health <= 0.0 && !M.M_CheckBottom(ent)))
- {
- vel= ent.velocity;
- speed= (float) Math.sqrt(vel[0] * vel[0] + vel[1] * vel[1]);
- if (speed != 0)
- {
- friction= Defines.sv_friction;
-
- control= speed < Defines.sv_stopspeed ? Defines.sv_stopspeed : speed;
- newspeed= speed - Defines.FRAMETIME * control * friction;
-
- if (newspeed < 0)
- newspeed= 0;
- newspeed /= speed;
-
- vel[0] *= newspeed;
- vel[1] *= newspeed;
- }
- }
-
- if ((ent.svflags & Defines.SVF_MONSTER) != 0)
- mask= Defines.MASK_MONSTERSOLID;
- else
- mask= Defines.MASK_SOLID;
-
- SV_FlyMove(ent, Defines.FRAMETIME, mask);
-
- GameBase.gi.linkentity(ent);
- GameBase.G_TouchTriggers(ent);
- if (!ent.inuse)
- return;
-
- if (ent.groundentity != null)
- if (!wasonground)
- if (hitsound)
- GameBase.gi.sound(ent, 0, GameBase.gi.soundindex("world/land.wav"), 1, 1, 0);
- }
-
- // regular thinking
- SV_RunThink(ent);
- }
-
- /*
- =============
- SV_movestep
-
- Called by monster program code.
- The move will be adjusted for slopes and stairs, but if the move isn't
- possible, no move is done, false is returned, and
- pr_global_struct.trace_normal is set to the normal of the blocking wall
- =============
- */
- // FIXME since we need to test end position contents here, can we avoid doing
- // it again later in catagorize position?
- public static boolean SV_movestep(edict_t ent, float[] move, boolean relink)
- {
- float dz;
- float[] oldorg= { 0, 0, 0 };
- float[] neworg= { 0, 0, 0 };
- float[] end= { 0, 0, 0 };
-
- trace_t trace= null; // = new trace_t();
- int i;
- float stepsize;
- float[] test= { 0, 0, 0 };
- int contents;
-
- // try the move
- Math3D.VectorCopy(ent.s.origin, oldorg);
- Math3D.VectorAdd(ent.s.origin, move, neworg);
-
- // flying monsters don't step up
- if ((ent.flags & (Defines.FL_SWIM | Defines.FL_FLY)) != 0)
- {
- // try one move with vertical motion, then one without
- for (i= 0; i < 2; i++)
- {
- Math3D.VectorAdd(ent.s.origin, move, neworg);
- if (i == 0 && ent.enemy != null)
- {
- if (ent.goalentity == null)
- ent.goalentity= ent.enemy;
- dz= ent.s.origin[2] - ent.goalentity.s.origin[2];
- if (ent.goalentity.client != null)
- {
- if (dz > 40)
- neworg[2] -= 8;
- if (!((ent.flags & Defines.FL_SWIM) != 0 && (ent.waterlevel < 2)))
- if (dz < 30)
- neworg[2] += 8;
- }
- else
- {
- if (dz > 8)
- neworg[2] -= 8;
- else if (dz > 0)
- neworg[2] -= dz;
- else if (dz < -8)
- neworg[2] += 8;
- else
- neworg[2] += dz;
- }
- }
- trace= GameBase.gi.trace(ent.s.origin, ent.mins, ent.maxs, neworg, ent, Defines.MASK_MONSTERSOLID);
-
- // fly monsters don't enter water voluntarily
- if ((ent.flags & Defines.FL_FLY) != 0)
- {
- if (ent.waterlevel == 0)
- {
- test[0]= trace.endpos[0];
- test[1]= trace.endpos[1];
- test[2]= trace.endpos[2] + ent.mins[2] + 1;
- contents= GameBase.gi.pointcontents.pointcontents(test);
- if ((contents & Defines.MASK_WATER) != 0)
- return false;
- }
- }
-
- // swim monsters don't exit water voluntarily
- if ((ent.flags & Defines.FL_SWIM) != 0)
- {
- if (ent.waterlevel < 2)
- {
- test[0]= trace.endpos[0];
- test[1]= trace.endpos[1];
- test[2]= trace.endpos[2] + ent.mins[2] + 1;
- contents= GameBase.gi.pointcontents.pointcontents(test);
- if ((contents & Defines.MASK_WATER) == 0)
- return false;
- }
- }
-
- if (trace.fraction == 1)
- {
- Math3D.VectorCopy(trace.endpos, ent.s.origin);
- if (relink)
- {
- GameBase.gi.linkentity(ent);
- GameBase.G_TouchTriggers(ent);
- }
- return true;
- }
-
- if (ent.enemy == null)
- break;
- }
-
- return false;
- }
-
- // push down from a step height above the wished position
- if ((ent.monsterinfo.aiflags & Defines.AI_NOSTEP) == 0)
- stepsize= GameBase.STEPSIZE;
- else
- stepsize= 1;
-
- neworg[2] += stepsize;
- Math3D.VectorCopy(neworg, end);
- end[2] -= stepsize * 2;
-
- trace= GameBase.gi.trace(neworg, ent.mins, ent.maxs, end, ent, Defines.MASK_MONSTERSOLID);
-
- if (trace.allsolid)
- return false;
-
- if (trace.startsolid)
- {
- neworg[2] -= stepsize;
- trace= GameBase.gi.trace(neworg, ent.mins, ent.maxs, end, ent, Defines.MASK_MONSTERSOLID);
- if (trace.allsolid || trace.startsolid)
- return false;
- }
-
- // don't go in to water
- if (ent.waterlevel == 0)
- {
- test[0]= trace.endpos[0];
- test[1]= trace.endpos[1];
- test[2]= trace.endpos[2] + ent.mins[2] + 1;
- contents= GameBase.gi.pointcontents.pointcontents(test);
-
- if ((contents & Defines.MASK_WATER) != 0)
- return false;
- }
-
- if (trace.fraction == 1)
- {
- // if monster had the ground pulled out, go ahead and fall
- if ((ent.flags & Defines.FL_PARTIALGROUND) != 0)
- {
- Math3D.VectorAdd(ent.s.origin, move, ent.s.origin);
- if (relink)
- {
- GameBase.gi.linkentity(ent);
- GameBase.G_TouchTriggers(ent);
- }
- ent.groundentity= null;
- return true;
- }
-
- return false; // walked off an edge
- }
-
- // check point traces down for dangling corners
- Math3D.VectorCopy(trace.endpos, ent.s.origin);
-
- if (!M.M_CheckBottom(ent))
- {
- if ((ent.flags & Defines.FL_PARTIALGROUND) != 0)
- {
- // entity had floor mostly pulled out from underneath it
- // and is trying to correct
- if (relink)
- {
- GameBase.gi.linkentity(ent);
- GameBase.G_TouchTriggers(ent);
- }
- return true;
- }
- Math3D.VectorCopy(oldorg, ent.s.origin);
- return false;
- }
-
- if ((ent.flags & Defines.FL_PARTIALGROUND) != 0)
- {
- ent.flags &= ~Defines.FL_PARTIALGROUND;
- }
- ent.groundentity= trace.ent;
- ent.groundentity_linkcount= trace.ent.linkcount;
-
- // the move is ok
- if (relink)
- {
- GameBase.gi.linkentity(ent);
- GameBase.G_TouchTriggers(ent);
- }
- return true;
- }
-
- /*
- ======================
- SV_StepDirection
-
- Turns to the movement direction, and walks the current distance if
- facing it.
-
- ======================
- */
- public static boolean SV_StepDirection(edict_t ent, float yaw, float dist)
- {
- float[] move= { 0, 0, 0 };
- float[] oldorigin= { 0, 0, 0 };
- float delta;
-
- ent.ideal_yaw= yaw;
- M.M_ChangeYaw(ent);
-
- yaw= (float) (yaw * Math.PI * 2 / 360);
- move[0]= (float) Math.cos(yaw) * dist;
- move[1]= (float) Math.sin(yaw) * dist;
- move[2]= 0;
-
- Math3D.VectorCopy(ent.s.origin, oldorigin);
- if (SV_movestep(ent, move, false))
- {
- delta= ent.s.angles[Defines.YAW] - ent.ideal_yaw;
- if (delta > 45 && delta < 315)
- { // not turned far enough, so don't take the step
- Math3D.VectorCopy(oldorigin, ent.s.origin);
- }
- GameBase.gi.linkentity(ent);
- GameBase.G_TouchTriggers(ent);
- return true;
- }
- GameBase.gi.linkentity(ent);
- GameBase.G_TouchTriggers(ent);
- return false;
- }
-
- /*
- ======================
- SV_FixCheckBottom
-
- ======================
- */
- public static void SV_FixCheckBottom(edict_t ent)
- {
- ent.flags |= Defines.FL_PARTIALGROUND;
- }
-
- public static void SV_NewChaseDir(edict_t actor, edict_t enemy, float dist)
- {
- float deltax, deltay;
- float d[]= { 0, 0, 0 };
- float tdir, olddir, turnaround;
-
- //FIXME: how did we get here with no enemy
- if (enemy == null)
- {
- Com.DPrintf("SV_NewChaseDir without enemy!\n");
- return;
- }
- olddir= Math3D.anglemod((int) (actor.ideal_yaw / 45) * 45);
- turnaround= Math3D.anglemod(olddir - 180);
-
- deltax= enemy.s.origin[0] - actor.s.origin[0];
- deltay= enemy.s.origin[1] - actor.s.origin[1];
- if (deltax > 10)
- d[1]= 0;
- else if (deltax < -10)
- d[1]= 180;
- else
- d[1]= GameBase.DI_NODIR;
- if (deltay < -10)
- d[2]= 270;
- else if (deltay > 10)
- d[2]= 90;
- else
- d[2]= GameBase.DI_NODIR;
-
- // try direct route
- if (d[1] != GameBase.DI_NODIR && d[2] != GameBase.DI_NODIR)
- {
- if (d[1] == 0)
- tdir= d[2] == 90 ? 45 : 315;
- else
- tdir= d[2] == 90 ? 135 : 215;
-
- if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
- return;
- }
-
- // try other directions
- if (((Lib.rand() & 3) & 1) != 0 || Math.abs(deltay) > Math.abs(deltax))
- {
- tdir= d[1];
- d[1]= d[2];
- d[2]= tdir;
- }
-
- if (d[1] != GameBase.DI_NODIR && d[1] != turnaround && SV_StepDirection(actor, d[1], dist))
- return;
-
- if (d[2] != GameBase.DI_NODIR && d[2] != turnaround && SV_StepDirection(actor, d[2], dist))
- return;
-
- /* there is no direct path to the player, so pick another direction */
-
- if (olddir != GameBase.DI_NODIR && SV_StepDirection(actor, olddir, dist))
- return;
-
- if ((Lib.rand() & 1) != 0) /*randomly determine direction of search*/
- {
- for (tdir= 0; tdir <= 315; tdir += 45)
- if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
- return;
- }
- else
- {
- for (tdir= 315; tdir >= 0; tdir -= 45)
- if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
- return;
- }
-
- if (turnaround != GameBase.DI_NODIR && SV_StepDirection(actor, turnaround, dist))
- return;
-
- actor.ideal_yaw= olddir; // can't move
-
- // if a bridge was pulled out from underneath a monster, it may not have
- // a valid standing position at all
-
- if (!M.M_CheckBottom(actor))
- SV_FixCheckBottom(actor);
- }
-
- /*
- ======================
- SV_CloseEnough
-
- ======================
- */ //ok
- public static boolean SV_CloseEnough(edict_t ent, edict_t goal, float dist)
- {
- int i;
-
- for (i= 0; i < 3; i++)
- {
- if (goal.absmin[i] > ent.absmax[i] + dist)
- return false;
- if (goal.absmax[i] < ent.absmin[i] - dist)
- return false;
- }
- return true;
- }
-}
+public final class SV {
+
+ ///////////////////////////////////////
+ public static edict_t[] SV_TestEntityPosition(edict_t ent) {
+ trace_t trace;
+ int mask;
+
+ if (ent.clipmask != 0)
+ mask = ent.clipmask;
+ else
+ mask = Defines.MASK_SOLID;
+
+ trace = GameBase.gi.trace(ent.s.origin, ent.mins, ent.maxs,
+ ent.s.origin, ent, mask);
+
+ if (trace.startsolid)
+ return GameBase.g_edicts;
+
+ return null;
+ }
+
+ ///////////////////////////////////////
+ public static void SV_CheckVelocity(edict_t ent) {
+ int i;
+
+ //
+ // bound velocity
+ //
+ for (i = 0; i < 3; i++) {
+ if (ent.velocity[i] > GameBase.sv_maxvelocity.value)
+ ent.velocity[i] = GameBase.sv_maxvelocity.value;
+ else if (ent.velocity[i] < -GameBase.sv_maxvelocity.value)
+ ent.velocity[i] = -GameBase.sv_maxvelocity.value;
+ }
+ }
+
+ /**
+ * Runs thinking code for this frame if necessary.
+ */
+ public static boolean SV_RunThink(edict_t ent) {
+ float thinktime;
+
+ thinktime = ent.nextthink;
+ if (thinktime <= 0)
+ return true;
+ if (thinktime > GameBase.level.time + 0.001)
+ return true;
+
+ ent.nextthink = 0;
+
+ if (ent.think == null)
+ Com.Error(Defines.ERR_FATAL, "NULL ent.think");
+
+ ent.think.think(ent);
+
+ return false;
+ }
+
+ /**
+ * Two entities have touched, so run their touch functions.
+ */
+ public static void SV_Impact(edict_t e1, trace_t trace) {
+ edict_t e2;
+
+ e2 = trace.ent;
+
+ if (e1.touch != null && e1.solid != Defines.SOLID_NOT)
+ e1.touch.touch(e1, e2, trace.plane, trace.surface);
+
+ if (e2.touch != null && e2.solid != Defines.SOLID_NOT)
+ e2.touch.touch(e2, e1, GameBase.dummyplane, null);
+ }
+
+ public static int SV_FlyMove(edict_t ent, float time, int mask) {
+ edict_t hit;
+ int bumpcount, numbumps;
+ float[] dir = { 0.0f, 0.0f, 0.0f };
+ float d;
+ int numplanes;
+ float[][] planes = new float[GameBase.MAX_CLIP_PLANES][3];
+ float[] primal_velocity = { 0.0f, 0.0f, 0.0f };
+ float[] original_velocity = { 0.0f, 0.0f, 0.0f };
+ float[] new_velocity = { 0.0f, 0.0f, 0.0f };
+ int i, j;
+ trace_t trace;
+ float[] end = { 0.0f, 0.0f, 0.0f };
+ float time_left;
+ int blocked;
+
+ numbumps = 4;
+
+ blocked = 0;
+ Math3D.VectorCopy(ent.velocity, original_velocity);
+ Math3D.VectorCopy(ent.velocity, primal_velocity);
+ numplanes = 0;
+
+ time_left = time;
+
+ ent.groundentity = null;
+ for (bumpcount = 0; bumpcount < numbumps; bumpcount++) {
+ for (i = 0; i < 3; i++)
+ end[i] = ent.s.origin[i] + time_left * ent.velocity[i];
+
+ trace = GameBase.gi.trace(ent.s.origin, ent.mins, ent.maxs, end,
+ ent, mask);
+
+ if (trace.allsolid) { // entity is trapped in another solid
+ Math3D.VectorCopy(Globals.vec3_origin, ent.velocity);
+ return 3;
+ }
+
+ if (trace.fraction > 0) { // actually covered some distance
+ Math3D.VectorCopy(trace.endpos, ent.s.origin);
+ Math3D.VectorCopy(ent.velocity, original_velocity);
+ numplanes = 0;
+ }
+
+ if (trace.fraction == 1)
+ break; // moved the entire distance
+
+ hit = trace.ent;
+
+ if (trace.plane.normal[2] > 0.7) {
+ blocked |= 1; // floor
+ if (hit.solid == Defines.SOLID_BSP) {
+ ent.groundentity = hit;
+ ent.groundentity_linkcount = hit.linkcount;
+ }
+ }
+ if (trace.plane.normal[2] == 0.0f) {
+ blocked |= 2; // step
+ }
+
+ //
+ // run the impact function
+ //
+ SV_Impact(ent, trace);
+ if (!ent.inuse)
+ break; // removed by the impact function
+
+ time_left -= time_left * trace.fraction;
+
+ // cliped to another plane
+ if (numplanes >= GameBase.MAX_CLIP_PLANES) { // this shouldn't
+ // really happen
+ Math3D.VectorCopy(Globals.vec3_origin, ent.velocity);
+ return 3;
+ }
+
+ Math3D.VectorCopy(trace.plane.normal, planes[numplanes]);
+ numplanes++;
+
+ //
+ // modify original_velocity so it parallels all of the clip planes
+ //
+ for (i = 0; i < numplanes; i++) {
+ GameBase.ClipVelocity(original_velocity, planes[i],
+ new_velocity, 1);
+
+ for (j = 0; j < numplanes; j++)
+ if ((j != i)
+ && Math3D.VectorCompare(planes[i], planes[j]) == 0.0f) {
+ if (Math3D.DotProduct(new_velocity, planes[j]) < 0)
+ break; // not ok
+ }
+ if (j == numplanes)
+ break;
+ }
+
+ if (i != numplanes) { // go along this plane
+ Math3D.VectorCopy(new_velocity, ent.velocity);
+ } else { // go along the crease
+ if (numplanes != 2) {
+ // gi.dprintf ("clip velocity, numplanes ==
+ // %i\n",numplanes);
+ Math3D.VectorCopy(Globals.vec3_origin, ent.velocity);
+ return 7;
+ }
+ Math3D.CrossProduct(planes[0], planes[1], dir);
+ d = Math3D.DotProduct(dir, ent.velocity);
+ Math3D.VectorScale(dir, d, ent.velocity);
+ }
+
+ //
+ // if original velocity is against the original velocity, stop dead
+ // to avoid tiny occilations in sloping corners
+ //
+ if (Math3D.DotProduct(ent.velocity, primal_velocity) <= 0) {
+ Math3D.VectorCopy(Globals.vec3_origin, ent.velocity);
+ return blocked;
+ }
+ }
+
+ return blocked;
+ }
+
+ /*
+ * ============ SV_AddGravity
+ *
+ * ============
+ */
+ public static void SV_AddGravity(edict_t ent) {
+ ent.velocity[2] -= ent.gravity * GameBase.sv_gravity.value
+ * Defines.FRAMETIME;
+ }
+
+ /**
+ * Does not change the entities velocity at all
+ */
+ public static trace_t SV_PushEntity(edict_t ent, float[] push) {
+ trace_t trace;
+ float[] start = { 0, 0, 0 };
+ float[] end = { 0, 0, 0 };
+ int mask;
+
+ Math3D.VectorCopy(ent.s.origin, start);
+ Math3D.VectorAdd(start, push, end);
+
+ // FIXME: test this
+ // a goto statement was replaced.
+ boolean retry = false;
+
+ do {
+ if (ent.clipmask != 0)
+ mask = ent.clipmask;
+ else
+ mask = Defines.MASK_SOLID;
+
+ trace = GameBase.gi
+ .trace(start, ent.mins, ent.maxs, end, ent, mask);
+
+ Math3D.VectorCopy(trace.endpos, ent.s.origin);
+ GameBase.gi.linkentity(ent);
+
+ retry = false;
+ if (trace.fraction != 1.0) {
+ SV_Impact(ent, trace);
+
+ // if the pushed entity went away and the pusher is still there
+ if (!trace.ent.inuse && ent.inuse) {
+ // move the pusher back and try again
+ Math3D.VectorCopy(start, ent.s.origin);
+ GameBase.gi.linkentity(ent);
+ //goto retry;
+ retry = true;
+ }
+ }
+ } while (retry);
+
+ if (ent.inuse)
+ GameBase.G_TouchTriggers(ent);
+
+ return trace;
+ }
+
+ /*
+ * ============ SV_Push
+ *
+ * Objects need to be moved back on a failed push, otherwise riders would
+ * continue to slide. ============
+ */
+ public static boolean SV_Push(edict_t pusher, float[] move, float[] amove) {
+ int i, e;
+ edict_t check, block[];
+ float[] mins = { 0, 0, 0 };
+ float[] maxs = { 0, 0, 0 };
+ pushed_t p;
+ float[] org = { 0, 0, 0 };
+ float[] org2 = { 0, 0, 0 };
+ float[] move2 = { 0, 0, 0 };
+ float[] forward = { 0, 0, 0 };
+ float[] right = { 0, 0, 0 };
+ float[] up = { 0, 0, 0 };
+
+ // clamp the move to 1/8 units, so the position will
+ // be accurate for client side prediction
+ for (i = 0; i < 3; i++) {
+ float temp;
+ temp = move[i] * 8.0f;
+ if (temp > 0.0)
+ temp += 0.5;
+ else
+ temp -= 0.5;
+ move[i] = 0.125f * (int) temp;
+ }
+
+ // find the bounding box
+ for (i = 0; i < 3; i++) {
+ mins[i] = pusher.absmin[i] + move[i];
+ maxs[i] = pusher.absmax[i] + move[i];
+ }
+
+ // we need this for pushing things later
+ Math3D.VectorSubtract(Globals.vec3_origin, amove, org);
+ Math3D.AngleVectors(org, forward, right, up);
+
+ // save the pusher's original position
+ GameBase.pushed[GameBase.pushed_p].ent = pusher;
+ Math3D.VectorCopy(pusher.s.origin,
+ GameBase.pushed[GameBase.pushed_p].origin);
+ Math3D.VectorCopy(pusher.s.angles,
+ GameBase.pushed[GameBase.pushed_p].angles);
+
+ if (pusher.client != null)
+ GameBase.pushed[GameBase.pushed_p].deltayaw = pusher.client.ps.pmove.delta_angles[Defines.YAW];
+
+ GameBase.pushed_p++;
+
+ // move the pusher to it's final position
+ Math3D.VectorAdd(pusher.s.origin, move, pusher.s.origin);
+ Math3D.VectorAdd(pusher.s.angles, amove, pusher.s.angles);
+ GameBase.gi.linkentity(pusher);
+
+ // see if any solid entities are inside the final position
+
+ //check= g_edicts + 1;
+ for (e = 1; e < GameBase.num_edicts; e++) {
+ check = GameBase.g_edicts[e];
+ if (!check.inuse)
+ continue;
+ if (check.movetype == Defines.MOVETYPE_PUSH
+ || check.movetype == Defines.MOVETYPE_STOP
+ || check.movetype == Defines.MOVETYPE_NONE
+ || check.movetype == Defines.MOVETYPE_NOCLIP)
+ continue;
+
+ if (check.area.prev == null)
+ continue; // not linked in anywhere
+
+ // if the entity is standing on the pusher, it will definitely be
+ // moved
+ if (check.groundentity != pusher) {
+ // see if the ent needs to be tested
+ if (check.absmin[0] >= maxs[0] || check.absmin[1] >= maxs[1]
+ || check.absmin[2] >= maxs[2]
+ || check.absmax[0] <= mins[0]
+ || check.absmax[1] <= mins[1]
+ || check.absmax[2] <= mins[2])
+ continue;
+
+ // see if the ent's bbox is inside the pusher's final position
+ if (SV_TestEntityPosition(check) == null)
+ continue;
+ }
+
+ if ((pusher.movetype == Defines.MOVETYPE_PUSH)
+ || (check.groundentity == pusher)) {
+ // move this entity
+ GameBase.pushed[GameBase.pushed_p].ent = check;
+ Math3D.VectorCopy(check.s.origin,
+ GameBase.pushed[GameBase.pushed_p].origin);
+ Math3D.VectorCopy(check.s.angles,
+ GameBase.pushed[GameBase.pushed_p].angles);
+ GameBase.pushed_p++;
+
+ // try moving the contacted entity
+ Math3D.VectorAdd(check.s.origin, move, check.s.origin);
+ if (check.client != null) { // FIXME: doesn't rotate monsters?
+ check.client.ps.pmove.delta_angles[Defines.YAW] += amove[Defines.YAW];
+ }
+
+ // figure movement due to the pusher's amove
+ Math3D.VectorSubtract(check.s.origin, pusher.s.origin, org);
+ org2[0] = Math3D.DotProduct(org, forward);
+ org2[1] = -Math3D.DotProduct(org, right);
+ org2[2] = Math3D.DotProduct(org, up);
+ Math3D.VectorSubtract(org2, org, move2);
+ Math3D.VectorAdd(check.s.origin, move2, check.s.origin);
+
+ // may have pushed them off an edge
+ if (check.groundentity != pusher)
+ check.groundentity = null;
+
+ block = SV_TestEntityPosition(check);
+ if (block == null) { // pushed ok
+ GameBase.gi.linkentity(check);
+ // impact?
+ continue;
+ }
+
+ // if it is ok to leave in the old position, do it
+ // this is only relevent for riding entities, not pushed
+ // FIXME: this doesn't acount for rotation
+ Math3D.VectorSubtract(check.s.origin, move, check.s.origin);
+ block = SV_TestEntityPosition(check);
+
+ if (block == null) {
+ GameBase.pushed_p--;
+ continue;
+ }
+ }
+
+ // save off the obstacle so we can call the block function
+ GameBase.obstacle = check;
+
+ // move back any entities we already moved
+ // go backwards, so if the same entity was pushed
+ // twice, it goes back to the original position
+ for (int ip = GameBase.pushed_p - 1; ip >= 0; ip--) {
+ p = GameBase.pushed[ip];
+ Math3D.VectorCopy(p.origin, p.ent.s.origin);
+ Math3D.VectorCopy(p.angles, p.ent.s.angles);
+ if (p.ent.client != null) {
+ p.ent.client.ps.pmove.delta_angles[Defines.YAW] = (short) p.deltayaw;
+ }
+ GameBase.gi.linkentity(p.ent);
+ }
+ return false;
+ }
+
+ // FIXME: is there a better way to handle this?
+ // see if anything we moved has touched a trigger
+ for (int ip = GameBase.pushed_p - 1; ip >= 0; ip--)
+ GameBase.G_TouchTriggers(GameBase.pushed[ip].ent);
+
+ return true;
+ }
+
+ /*
+ * ================ SV_Physics_Pusher
+ *
+ * Bmodel objects don't interact with each other, but push all box objects
+ * ================
+ */
+ public static void SV_Physics_Pusher(edict_t ent) {
+ float[] move = { 0, 0, 0 };
+ float[] amove = { 0, 0, 0 };
+ edict_t part, mv;
+
+ // if not a team captain, so movement will be handled elsewhere
+ if ((ent.flags & Defines.FL_TEAMSLAVE) != 0)
+ return;
+
+ // make sure all team slaves can move before commiting
+ // any moves or calling any think functions
+ // if the move is blocked, all moved objects will be backed out
+ // retry:
+ GameBase.pushed_p = 0;
+ for (part = ent; part != null; part = part.teamchain) {
+ if (part.velocity[0] != 0 || part.velocity[1] != 0
+ || part.velocity[2] != 0 || part.avelocity[0] != 0
+ || part.avelocity[1] != 0 || part.avelocity[2] != 0) { // object
+ // is
+ // moving
+ Math3D.VectorScale(part.velocity, Defines.FRAMETIME, move);
+ Math3D.VectorScale(part.avelocity, Defines.FRAMETIME, amove);
+
+ if (!SV_Push(part, move, amove))
+ break; // move was blocked
+ }
+ }
+ if (GameBase.pushed_p > Defines.MAX_EDICTS)
+ SV_GAME.PF_error(Defines.ERR_FATAL,
+ "pushed_p > &pushed[MAX_EDICTS], memory corrupted");
+
+ if (part != null) {
+ // the move failed, bump all nextthink times and back out moves
+ for (mv = ent; mv != null; mv = mv.teamchain) {
+ if (mv.nextthink > 0)
+ mv.nextthink += Defines.FRAMETIME;
+ }
+
+ // if the pusher has a "blocked" function, call it
+ // otherwise, just stay in place until the obstacle is gone
+ if (part.blocked != null)
+ part.blocked.blocked(part, GameBase.obstacle);
+ } else { // the move succeeded, so call all think functions
+ for (part = ent; part != null; part = part.teamchain) {
+ SV_RunThink(part);
+ }
+ }
+ }
+
+ // ==================================================================
+
+ /*
+ * ============= SV_Physics_None
+ *
+ * Non moving objects can only think =============
+ */
+ public static void SV_Physics_None(edict_t ent) {
+ // regular thinking
+ SV_RunThink(ent);
+ }
+
+ /*
+ * ============= SV_Physics_Noclip
+ *
+ * A moving object that doesn't obey physics =============
+ */
+ public static void SV_Physics_Noclip(edict_t ent) {
+ // regular thinking
+ if (!SV_RunThink(ent))
+ return;
+
+ Math3D.VectorMA(ent.s.angles, Defines.FRAMETIME, ent.avelocity,
+ ent.s.angles);
+ Math3D.VectorMA(ent.s.origin, Defines.FRAMETIME, ent.velocity,
+ ent.s.origin);
+
+ GameBase.gi.linkentity(ent);
+ }
+
+ /*
+ * ==============================================================================
+ *
+ * TOSS / BOUNCE
+ *
+ * ==============================================================================
+ */
+
+ /*
+ * ============= SV_Physics_Toss
+ *
+ * Toss, bounce, and fly movement. When onground, do nothing. =============
+ */
+ public static void SV_Physics_Toss(edict_t ent) {
+
+ trace_t trace;
+ float[] move = { 0, 0, 0 };
+ float backoff;
+ edict_t slave;
+ boolean wasinwater;
+ boolean isinwater;
+ float[] old_origin = { 0, 0, 0 };
+
+ // regular thinking
+ SV_RunThink(ent);
+
+ // if not a team captain, so movement will be handled elsewhere
+ if ((ent.flags & Defines.FL_TEAMSLAVE) != 0)
+ return;
+
+ if (ent.velocity[2] > 0)
+ ent.groundentity = null;
+
+ // check for the groundentity going away
+ if (ent.groundentity != null)
+ if (!ent.groundentity.inuse)
+ ent.groundentity = null;
+
+ // if onground, return without moving
+ if (ent.groundentity != null)
+ return;
+
+ Math3D.VectorCopy(ent.s.origin, old_origin);
+
+ SV_CheckVelocity(ent);
+
+ // add gravity
+ if (ent.movetype != Defines.MOVETYPE_FLY
+ && ent.movetype != Defines.MOVETYPE_FLYMISSILE)
+ SV_AddGravity(ent);
+
+ // move angles
+ Math3D.VectorMA(ent.s.angles, Defines.FRAMETIME, ent.avelocity,
+ ent.s.angles);
+
+ // move origin
+ Math3D.VectorScale(ent.velocity, Defines.FRAMETIME, move);
+ trace = SV_PushEntity(ent, move);
+ if (!ent.inuse)
+ return;
+
+ if (trace.fraction < 1) {
+ if (ent.movetype == Defines.MOVETYPE_BOUNCE)
+ backoff = 1.5f;
+ else
+ backoff = 1;
+
+ GameBase.ClipVelocity(ent.velocity, trace.plane.normal,
+ ent.velocity, backoff);
+
+ // stop if on ground
+ if (trace.plane.normal[2] > 0.7) {
+ if (ent.velocity[2] < 60
+ || ent.movetype != Defines.MOVETYPE_BOUNCE) {
+ ent.groundentity = trace.ent;
+ ent.groundentity_linkcount = trace.ent.linkcount;
+ Math3D.VectorCopy(Globals.vec3_origin, ent.velocity);
+ Math3D.VectorCopy(Globals.vec3_origin, ent.avelocity);
+ }
+ }
+
+ // if (ent.touch)
+ // ent.touch (ent, trace.ent, &trace.plane, trace.surface);
+ }
+
+ // check for water transition
+ wasinwater = (ent.watertype & Defines.MASK_WATER) != 0;
+ ent.watertype = GameBase.gi.pointcontents.pointcontents(ent.s.origin);
+ isinwater = (ent.watertype & Defines.MASK_WATER) != 0;
+
+ if (isinwater)
+ ent.waterlevel = 1;
+ else
+ ent.waterlevel = 0;
+
+ if (!wasinwater && isinwater)
+ GameBase.gi.positioned_sound(old_origin, ent, Defines.CHAN_AUTO,
+ GameBase.gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
+ else if (wasinwater && !isinwater)
+ GameBase.gi.positioned_sound(ent.s.origin, ent, Defines.CHAN_AUTO,
+ GameBase.gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
+
+ // move teamslaves
+ for (slave = ent.teamchain; slave != null; slave = slave.teamchain) {
+ Math3D.VectorCopy(ent.s.origin, slave.s.origin);
+ GameBase.gi.linkentity(slave);
+ }
+ }
+
+ /*
+ * ===============================================================================
+ *
+ * STEPPING MOVEMENT
+ *
+ * ===============================================================================
+ */
+
+ /*
+ * ============= SV_Physics_Step
+ *
+ * Monsters freefall when they don't have a ground entity, otherwise all
+ * movement is done with discrete steps.
+ *
+ * This is also used for objects that have become still on the ground, but
+ * will fall if the floor is pulled out from under them. FIXME: is this
+ * true? =============
+ */
+
+ // FIXME: hacked in for E3 demo
+ public static void SV_AddRotationalFriction(edict_t ent) {
+ int n;
+ float adjustment;
+
+ Math3D.VectorMA(ent.s.angles, Defines.FRAMETIME, ent.avelocity,
+ ent.s.angles);
+ adjustment = Defines.FRAMETIME * Defines.sv_stopspeed
+ * Defines.sv_friction;
+ for (n = 0; n < 3; n++) {
+ if (ent.avelocity[n] > 0) {
+ ent.avelocity[n] -= adjustment;
+ if (ent.avelocity[n] < 0)
+ ent.avelocity[n] = 0;
+ } else {
+ ent.avelocity[n] += adjustment;
+ if (ent.avelocity[n] > 0)
+ ent.avelocity[n] = 0;
+ }
+ }
+ }
+
+ public static void SV_Physics_Step(edict_t ent) {
+ boolean wasonground;
+ boolean hitsound = false;
+ float vel[];
+ float speed, newspeed, control;
+ float friction;
+ edict_t groundentity;
+ int mask;
+
+ // airborn monsters should always check for ground
+ if (ent.groundentity == null)
+ M.M_CheckGround(ent);
+
+ groundentity = ent.groundentity;
+
+ SV_CheckVelocity(ent);
+
+ if (groundentity != null)
+ wasonground = true;
+ else
+ wasonground = false;
+
+ if (ent.avelocity[0] != 0 || ent.avelocity[1] != 0
+ || ent.avelocity[2] != 0)
+ SV_AddRotationalFriction(ent);
+
+ // add gravity except:
+ // flying monsters
+ // swimming monsters who are in the water
+ if (!wasonground)
+ if (0 == (ent.flags & Defines.FL_FLY))
+ if (!((ent.flags & Defines.FL_SWIM) != 0 && (ent.waterlevel > 2))) {
+ if (ent.velocity[2] < GameBase.sv_gravity.value * -0.1)
+ hitsound = true;
+ if (ent.waterlevel == 0)
+ SV_AddGravity(ent);
+ }
+
+ // friction for flying monsters that have been given vertical velocity
+ if ((ent.flags & Defines.FL_FLY) != 0 && (ent.velocity[2] != 0)) {
+ speed = Math.abs(ent.velocity[2]);
+ control = speed < Defines.sv_stopspeed ? Defines.sv_stopspeed
+ : speed;
+ friction = Defines.sv_friction / 3;
+ newspeed = speed - (Defines.FRAMETIME * control * friction);
+ if (newspeed < 0)
+ newspeed = 0;
+ newspeed /= speed;
+ ent.velocity[2] *= newspeed;
+ }
+
+ // friction for flying monsters that have been given vertical velocity
+ if ((ent.flags & Defines.FL_SWIM) != 0 && (ent.velocity[2] != 0)) {
+ speed = Math.abs(ent.velocity[2]);
+ control = speed < Defines.sv_stopspeed ? Defines.sv_stopspeed
+ : speed;
+ newspeed = speed
+ - (Defines.FRAMETIME * control * Defines.sv_waterfriction * ent.waterlevel);
+ if (newspeed < 0)
+ newspeed = 0;
+ newspeed /= speed;
+ ent.velocity[2] *= newspeed;
+ }
+
+ if (ent.velocity[2] != 0 || ent.velocity[1] != 0
+ || ent.velocity[0] != 0) {
+ // apply friction
+ // let dead monsters who aren't completely onground slide
+ if ((wasonground)
+ || 0 != (ent.flags & (Defines.FL_SWIM | Defines.FL_FLY)))
+ if (!(ent.health <= 0.0 && !M.M_CheckBottom(ent))) {
+ vel = ent.velocity;
+ speed = (float) Math
+ .sqrt(vel[0] * vel[0] + vel[1] * vel[1]);
+ if (speed != 0) {
+ friction = Defines.sv_friction;
+
+ control = speed < Defines.sv_stopspeed ? Defines.sv_stopspeed
+ : speed;
+ newspeed = speed - Defines.FRAMETIME * control
+ * friction;
+
+ if (newspeed < 0)
+ newspeed = 0;
+ newspeed /= speed;
+
+ vel[0] *= newspeed;
+ vel[1] *= newspeed;
+ }
+ }
+
+ if ((ent.svflags & Defines.SVF_MONSTER) != 0)
+ mask = Defines.MASK_MONSTERSOLID;
+ else
+ mask = Defines.MASK_SOLID;
+
+ SV_FlyMove(ent, Defines.FRAMETIME, mask);
+
+ GameBase.gi.linkentity(ent);
+ GameBase.G_TouchTriggers(ent);
+ if (!ent.inuse)
+ return;
+
+ if (ent.groundentity != null)
+ if (!wasonground)
+ if (hitsound)
+ GameBase.gi.sound(ent, 0, GameBase.gi
+ .soundindex("world/land.wav"), 1, 1, 0);
+ }
+
+ // regular thinking
+ SV_RunThink(ent);
+ }
+
+ /*
+ * ============= SV_movestep
+ *
+ * Called by monster program code. The move will be adjusted for slopes and
+ * stairs, but if the move isn't possible, no move is done, false is
+ * returned, and pr_global_struct.trace_normal is set to the normal of the
+ * blocking wall =============
+ */
+ // FIXME since we need to test end position contents here, can we avoid
+ // doing
+ // it again later in catagorize position?
+ public static boolean SV_movestep(edict_t ent, float[] move, boolean relink) {
+ float dz;
+ float[] oldorg = { 0, 0, 0 };
+ float[] neworg = { 0, 0, 0 };
+ float[] end = { 0, 0, 0 };
+
+ trace_t trace = null; // = new trace_t();
+ int i;
+ float stepsize;
+ float[] test = { 0, 0, 0 };
+ int contents;
+
+ // try the move
+ Math3D.VectorCopy(ent.s.origin, oldorg);
+ Math3D.VectorAdd(ent.s.origin, move, neworg);
+
+ // flying monsters don't step up
+ if ((ent.flags & (Defines.FL_SWIM | Defines.FL_FLY)) != 0) {
+ // try one move with vertical motion, then one without
+ for (i = 0; i < 2; i++) {
+ Math3D.VectorAdd(ent.s.origin, move, neworg);
+ if (i == 0 && ent.enemy != null) {
+ if (ent.goalentity == null)
+ ent.goalentity = ent.enemy;
+ dz = ent.s.origin[2] - ent.goalentity.s.origin[2];
+ if (ent.goalentity.client != null) {
+ if (dz > 40)
+ neworg[2] -= 8;
+ if (!((ent.flags & Defines.FL_SWIM) != 0 && (ent.waterlevel < 2)))
+ if (dz < 30)
+ neworg[2] += 8;
+ } else {
+ if (dz > 8)
+ neworg[2] -= 8;
+ else if (dz > 0)
+ neworg[2] -= dz;
+ else if (dz < -8)
+ neworg[2] += 8;
+ else
+ neworg[2] += dz;
+ }
+ }
+ trace = GameBase.gi.trace(ent.s.origin, ent.mins, ent.maxs,
+ neworg, ent, Defines.MASK_MONSTERSOLID);
+
+ // fly monsters don't enter water voluntarily
+ if ((ent.flags & Defines.FL_FLY) != 0) {
+ if (ent.waterlevel == 0) {
+ test[0] = trace.endpos[0];
+ test[1] = trace.endpos[1];
+ test[2] = trace.endpos[2] + ent.mins[2] + 1;
+ contents = GameBase.gi.pointcontents
+ .pointcontents(test);
+ if ((contents & Defines.MASK_WATER) != 0)
+ return false;
+ }
+ }
+
+ // swim monsters don't exit water voluntarily
+ if ((ent.flags & Defines.FL_SWIM) != 0) {
+ if (ent.waterlevel < 2) {
+ test[0] = trace.endpos[0];
+ test[1] = trace.endpos[1];
+ test[2] = trace.endpos[2] + ent.mins[2] + 1;
+ contents = GameBase.gi.pointcontents
+ .pointcontents(test);
+ if ((contents & Defines.MASK_WATER) == 0)
+ return false;
+ }
+ }
+
+ if (trace.fraction == 1) {
+ Math3D.VectorCopy(trace.endpos, ent.s.origin);
+ if (relink) {
+ GameBase.gi.linkentity(ent);
+ GameBase.G_TouchTriggers(ent);
+ }
+ return true;
+ }
+
+ if (ent.enemy == null)
+ break;
+ }
+
+ return false;
+ }
+
+ // push down from a step height above the wished position
+ if ((ent.monsterinfo.aiflags & Defines.AI_NOSTEP) == 0)
+ stepsize = GameBase.STEPSIZE;
+ else
+ stepsize = 1;
+
+ neworg[2] += stepsize;
+ Math3D.VectorCopy(neworg, end);
+ end[2] -= stepsize * 2;
+
+ trace = GameBase.gi.trace(neworg, ent.mins, ent.maxs, end, ent,
+ Defines.MASK_MONSTERSOLID);
+
+ if (trace.allsolid)
+ return false;
+
+ if (trace.startsolid) {
+ neworg[2] -= stepsize;
+ trace = GameBase.gi.trace(neworg, ent.mins, ent.maxs, end, ent,
+ Defines.MASK_MONSTERSOLID);
+ if (trace.allsolid || trace.startsolid)
+ return false;
+ }
+
+ // don't go in to water
+ if (ent.waterlevel == 0) {
+ test[0] = trace.endpos[0];
+ test[1] = trace.endpos[1];
+ test[2] = trace.endpos[2] + ent.mins[2] + 1;
+ contents = GameBase.gi.pointcontents.pointcontents(test);
+
+ if ((contents & Defines.MASK_WATER) != 0)
+ return false;
+ }
+
+ if (trace.fraction == 1) {
+ // if monster had the ground pulled out, go ahead and fall
+ if ((ent.flags & Defines.FL_PARTIALGROUND) != 0) {
+ Math3D.VectorAdd(ent.s.origin, move, ent.s.origin);
+ if (relink) {
+ GameBase.gi.linkentity(ent);
+ GameBase.G_TouchTriggers(ent);
+ }
+ ent.groundentity = null;
+ return true;
+ }
+
+ return false; // walked off an edge
+ }
+
+ // check point traces down for dangling corners
+ Math3D.VectorCopy(trace.endpos, ent.s.origin);
+
+ if (!M.M_CheckBottom(ent)) {
+ if ((ent.flags & Defines.FL_PARTIALGROUND) != 0) {
+ // entity had floor mostly pulled out from underneath it
+ // and is trying to correct
+ if (relink) {
+ GameBase.gi.linkentity(ent);
+ GameBase.G_TouchTriggers(ent);
+ }
+ return true;
+ }
+ Math3D.VectorCopy(oldorg, ent.s.origin);
+ return false;
+ }
+
+ if ((ent.flags & Defines.FL_PARTIALGROUND) != 0) {
+ ent.flags &= ~Defines.FL_PARTIALGROUND;
+ }
+ ent.groundentity = trace.ent;
+ ent.groundentity_linkcount = trace.ent.linkcount;
+
+ // the move is ok
+ if (relink) {
+ GameBase.gi.linkentity(ent);
+ GameBase.G_TouchTriggers(ent);
+ }
+ return true;
+ }
+
+ /*
+ * ====================== SV_StepDirection
+ *
+ * Turns to the movement direction, and walks the current distance if facing
+ * it.
+ *
+ * ======================
+ */
+ public static boolean SV_StepDirection(edict_t ent, float yaw, float dist) {
+ float[] move = { 0, 0, 0 };
+ float[] oldorigin = { 0, 0, 0 };
+ float delta;
+
+ ent.ideal_yaw = yaw;
+ M.M_ChangeYaw(ent);
+
+ yaw = (float) (yaw * Math.PI * 2 / 360);
+ move[0] = (float) Math.cos(yaw) * dist;
+ move[1] = (float) Math.sin(yaw) * dist;
+ move[2] = 0;
+
+ Math3D.VectorCopy(ent.s.origin, oldorigin);
+ if (SV_movestep(ent, move, false)) {
+ delta = ent.s.angles[Defines.YAW] - ent.ideal_yaw;
+ if (delta > 45 && delta < 315) { // not turned far enough, so don't
+ // take the step
+ Math3D.VectorCopy(oldorigin, ent.s.origin);
+ }
+ GameBase.gi.linkentity(ent);
+ GameBase.G_TouchTriggers(ent);
+ return true;
+ }
+ GameBase.gi.linkentity(ent);
+ GameBase.G_TouchTriggers(ent);
+ return false;
+ }
+
+ /*
+ * ====================== SV_FixCheckBottom
+ *
+ * ======================
+ */
+ public static void SV_FixCheckBottom(edict_t ent) {
+ ent.flags |= Defines.FL_PARTIALGROUND;
+ }
+
+ public static void SV_NewChaseDir(edict_t actor, edict_t enemy, float dist) {
+ float deltax, deltay;
+ float d[] = { 0, 0, 0 };
+ float tdir, olddir, turnaround;
+
+ //FIXME: how did we get here with no enemy
+ if (enemy == null) {
+ Com.DPrintf("SV_NewChaseDir without enemy!\n");
+ return;
+ }
+ olddir = Math3D.anglemod((int) (actor.ideal_yaw / 45) * 45);
+ turnaround = Math3D.anglemod(olddir - 180);
+
+ deltax = enemy.s.origin[0] - actor.s.origin[0];
+ deltay = enemy.s.origin[1] - actor.s.origin[1];
+ if (deltax > 10)
+ d[1] = 0;
+ else if (deltax < -10)
+ d[1] = 180;
+ else
+ d[1] = GameBase.DI_NODIR;
+ if (deltay < -10)
+ d[2] = 270;
+ else if (deltay > 10)
+ d[2] = 90;
+ else
+ d[2] = GameBase.DI_NODIR;
+
+ // try direct route
+ if (d[1] != GameBase.DI_NODIR && d[2] != GameBase.DI_NODIR) {
+ if (d[1] == 0)
+ tdir = d[2] == 90 ? 45 : 315;
+ else
+ tdir = d[2] == 90 ? 135 : 215;
+
+ if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
+ return;
+ }
+
+ // try other directions
+ if (((Lib.rand() & 3) & 1) != 0 || Math.abs(deltay) > Math.abs(deltax)) {
+ tdir = d[1];
+ d[1] = d[2];
+ d[2] = tdir;
+ }
+
+ if (d[1] != GameBase.DI_NODIR && d[1] != turnaround
+ && SV_StepDirection(actor, d[1], dist))
+ return;
+
+ if (d[2] != GameBase.DI_NODIR && d[2] != turnaround
+ && SV_StepDirection(actor, d[2], dist))
+ return;
+
+ /* there is no direct path to the player, so pick another direction */
+
+ if (olddir != GameBase.DI_NODIR
+ && SV_StepDirection(actor, olddir, dist))
+ return;
+
+ if ((Lib.rand() & 1) != 0) /* randomly determine direction of search */{
+ for (tdir = 0; tdir <= 315; tdir += 45)
+ if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
+ return;
+ } else {
+ for (tdir = 315; tdir >= 0; tdir -= 45)
+ if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
+ return;
+ }
+
+ if (turnaround != GameBase.DI_NODIR
+ && SV_StepDirection(actor, turnaround, dist))
+ return;
+
+ actor.ideal_yaw = olddir; // can't move
+
+ // if a bridge was pulled out from underneath a monster, it may not have
+ // a valid standing position at all
+
+ if (!M.M_CheckBottom(actor))
+ SV_FixCheckBottom(actor);
+ }
+
+ /*
+ * ====================== SV_CloseEnough
+ *
+ * ======================
+ *///ok
+ public static boolean SV_CloseEnough(edict_t ent, edict_t goal, float dist) {
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (goal.absmin[i] > ent.absmax[i] + dist)
+ return false;
+ if (goal.absmax[i] < ent.absmin[i] - dist)
+ return false;
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/src/jake2/server/SV_CCMDS.java b/src/jake2/server/SV_CCMDS.java
index e6fd004..38e296e 100644
--- a/src/jake2/server/SV_CCMDS.java
+++ b/src/jake2/server/SV_CCMDS.java
@@ -19,22 +19,41 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// Created on 18.01.2004 by RST.
-// $Id: SV_CCMDS.java,v 1.11 2004-09-10 19:02:56 salomo Exp $
+// $Id: SV_CCMDS.java,v 1.12 2004-09-22 19:22:12 salomo Exp $
package jake2.server;
+import jake2.Defines;
import jake2.Globals;
-import jake2.game.*;
-import jake2.qcommon.*;
+import jake2.game.Cmd;
+import jake2.game.EndianHandler;
+import jake2.game.GameSVCmds;
+import jake2.game.GameSave;
+import jake2.game.Info;
+import jake2.game.cvar_t;
+import jake2.qcommon.CM;
+import jake2.qcommon.Com;
+import jake2.qcommon.Cvar;
+import jake2.qcommon.FS;
+import jake2.qcommon.MSG;
+import jake2.qcommon.Netchan;
+import jake2.qcommon.SZ;
+import jake2.qcommon.netadr_t;
+import jake2.qcommon.sizebuf_t;
+import jake2.qcommon.xcommand_t;
import jake2.sys.NET;
import jake2.sys.Sys;
+import jake2.util.Lib;
import jake2.util.QuakeFile;
import jake2.util.Vargs;
-import java.io.*;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
import java.util.Calendar;
-public class SV_CCMDS extends SV_ENTS {
+public class SV_CCMDS {
/*
===============================================================================
@@ -56,7 +75,7 @@ public class SV_CCMDS extends SV_ENTS {
int i, slot;
// only dedicated servers send heartbeats
- if (dedicated.value == 0) {
+ if (Globals.dedicated.value == 0) {
Com.Printf("Only dedicated servers use masters.\n");
return;
}
@@ -64,34 +83,31 @@ public class SV_CCMDS extends SV_ENTS {
// make sure the server is listed public
Cvar.Set("public", "1");
- for (i= 1; i < MAX_MASTERS; i++)
- master_adr[i]= new netadr_t();
+ for (i = 1; i < Defines.MAX_MASTERS; i++)
+ SV_MAIN.master_adr[i] = new netadr_t();
- slot= 1; // slot 0 will always contain the id master
- for (i= 1; i < Cmd.Argc(); i++) {
- if (slot == MAX_MASTERS)
+ slot = 1; // slot 0 will always contain the id master
+ for (i = 1; i < Cmd.Argc(); i++) {
+ if (slot == Defines.MAX_MASTERS)
break;
- if (!NET.StringToAdr(Cmd.Argv(i), master_adr[i])) {
+ if (!NET.StringToAdr(Cmd.Argv(i), SV_MAIN.master_adr[i])) {
Com.Printf("Bad address: " + Cmd.Argv(i) + "\n");
continue;
}
- if (master_adr[slot].port == 0)
- master_adr[slot].port= //BigShort (PORT_MASTER);
- PORT_MASTER;
-
- Com.Printf("Master server at " + NET.AdrToString(master_adr[slot]) + "\n");
+ if (SV_MAIN.master_adr[slot].port == 0)
+ SV_MAIN.master_adr[slot].port = Defines.PORT_MASTER;
+ Com.Printf("Master server at " + NET.AdrToString(SV_MAIN.master_adr[slot]) + "\n");
Com.Printf("Sending a ping.\n");
- Netchan.OutOfBandPrint(NS_SERVER, master_adr[slot], "ping");
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, SV_MAIN.master_adr[slot], "ping");
slot++;
}
- svs.last_heartbeat= -9999999;
+ SV_INIT.svs.last_heartbeat = -9999999;
}
-
/*
==================
SV_SetPlayer
@@ -108,19 +124,19 @@ public class SV_CCMDS extends SV_ENTS {
if (Cmd.Argc() < 2)
return false;
- s= Cmd.Argv(1);
+ s = Cmd.Argv(1);
// numeric values are just slot numbers
if (s.charAt(0) >= '0' && s.charAt(0) <= '9') {
- idnum= atoi(Cmd.Argv(1));
- if (idnum < 0 || idnum >= maxclients.value) {
+ idnum = Lib.atoi(Cmd.Argv(1));
+ if (idnum < 0 || idnum >= SV_MAIN.maxclients.value) {
Com.Printf("Bad client slot: " + idnum + "\n");
return false;
}
- sv_client= svs.clients[idnum];
- sv_player= sv_client.edict;
- if (0 == sv_client.state) {
+ SV_MAIN.sv_client = SV_INIT.svs.clients[idnum];
+ SV_USER.sv_player = SV_MAIN.sv_client.edict;
+ if (0 == SV_MAIN.sv_client.state) {
Com.Printf("Client " + idnum + " is not active\n");
return false;
}
@@ -128,13 +144,13 @@ public class SV_CCMDS extends SV_ENTS {
}
// check for a name match
- for (i= 0; i < maxclients.value; i++) {
- cl= svs.clients[i];
+ for (i = 0; i < SV_MAIN.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
if (0 == cl.state)
continue;
- if (0 == strcmp(cl.name, s)) {
- sv_client= cl;
- sv_player= sv_client.edict;
+ if (0 == Lib.strcmp(cl.name, s)) {
+ SV_MAIN.sv_client = cl;
+ SV_USER.sv_player = SV_MAIN.sv_client.edict;
return true;
}
}
@@ -142,7 +158,6 @@ public class SV_CCMDS extends SV_ENTS {
Com.Printf("Userid " + s + " is not on the server\n");
return false;
}
-
/*
===============================================================================
@@ -158,7 +173,6 @@ public class SV_CCMDS extends SV_ENTS {
catch (Exception e) {
}
}
-
/*
=====================
SV_WipeSavegame
@@ -172,32 +186,31 @@ public class SV_CCMDS extends SV_ENTS {
Com.DPrintf("SV_WipeSaveGame(" + savename + ")\n");
- name= FS.Gamedir() + "/save/" + savename + "/server.ssv";
+ name = FS.Gamedir() + "/save/" + savename + "/server.ssv";
remove(name);
- name= FS.Gamedir() + "/save/" + savename + "/game.ssv";
+ name = FS.Gamedir() + "/save/" + savename + "/game.ssv";
remove(name);
- name= FS.Gamedir() + "/save/" + savename + "/*.sav";
+ name = FS.Gamedir() + "/save/" + savename + "/*.sav";
- File f= Sys.FindFirst(name, 0, 0);
+ File f = Sys.FindFirst(name, 0, 0);
while (f != null) {
f.delete();
- f= Sys.FindNext();
+ f = Sys.FindNext();
}
Sys.FindClose();
- name= FS.Gamedir() + "/save/" + savename + "/*.sv2";
+ name = FS.Gamedir() + "/save/" + savename + "/*.sv2";
- f= Sys.FindFirst(name, 0, 0);
+ f = Sys.FindFirst(name, 0, 0);
while (f != null) {
f.delete();
- f= Sys.FindNext();
+ f = Sys.FindNext();
}
Sys.FindClose();
}
-
/*
================
CopyFile
@@ -205,18 +218,18 @@ public class SV_CCMDS extends SV_ENTS {
*/
public static void CopyFile(String src, String dst) {
RandomAccessFile f1, f2;
- int l= -1;
- byte buffer[]= new byte[65536];
+ int l = -1;
+ byte buffer[] = new byte[65536];
//Com.DPrintf("CopyFile (" + src + ", " + dst + ")\n");
try {
- f1= new RandomAccessFile(src, "r");
+ f1 = new RandomAccessFile(src, "r");
}
catch (Exception e) {
return;
}
try {
- f2= new RandomAccessFile(dst, "rw");
+ f2 = new RandomAccessFile(dst, "rw");
}
catch (Exception e) {
try {
@@ -231,7 +244,7 @@ public class SV_CCMDS extends SV_ENTS {
while (true) {
try {
- l= f1.read(buffer, 0, 65536);
+ l = f1.read(buffer, 0, 65536);
}
catch (IOException e1) {
@@ -263,7 +276,6 @@ public class SV_CCMDS extends SV_ENTS {
e2.printStackTrace();
}
}
-
/*
================
SV_CopySaveGame
@@ -281,38 +293,37 @@ public class SV_CCMDS extends SV_ENTS {
SV_WipeSavegame(dst);
// copy the savegame over
- name= FS.Gamedir() + "/save/" + src + "/server.ssv";
- name2= FS.Gamedir() + "/save/" + dst + "/server.ssv";
+ name = FS.Gamedir() + "/save/" + src + "/server.ssv";
+ name2 = FS.Gamedir() + "/save/" + dst + "/server.ssv";
FS.CreatePath(name2);
CopyFile(name, name2);
- name= FS.Gamedir() + "/save/" + src + "/game.ssv";
- name2= FS.Gamedir() + "/save/" + dst + "/game.ssv";
+ name = FS.Gamedir() + "/save/" + src + "/game.ssv";
+ name2 = FS.Gamedir() + "/save/" + dst + "/game.ssv";
CopyFile(name, name2);
- String name1= FS.Gamedir() + "/save/" + src + "/";
- len= name1.length();
- name= FS.Gamedir() + "/save/" + src + "/*.sav";
+ String name1 = FS.Gamedir() + "/save/" + src + "/";
+ len = name1.length();
+ name = FS.Gamedir() + "/save/" + src + "/*.sav";
- found= Sys.FindFirst(name, 0, 0);
+ found = Sys.FindFirst(name, 0, 0);
while (found != null) {
- name= name1 + found.getName();
- name2= FS.Gamedir() + "/save/" + dst + "/" + found.getName();
+ name = name1 + found.getName();
+ name2 = FS.Gamedir() + "/save/" + dst + "/" + found.getName();
CopyFile(name, name2);
// change sav to sv2
- name= name.substring(0, name.length() - 3) + "sv2";
- name2= name2.substring(0, name2.length() - 3) + "sv2";
+ name = name.substring(0, name.length() - 3) + "sv2";
+ name2 = name2.substring(0, name2.length() - 3) + "sv2";
CopyFile(name, name2);
- found= Sys.FindNext();
+ found = Sys.FindNext();
}
Sys.FindClose();
}
-
/*
==============
SV_WriteLevelFile
@@ -326,13 +337,13 @@ public class SV_CCMDS extends SV_ENTS {
Com.DPrintf("SV_WriteLevelFile()\n");
- name= FS.Gamedir() + "/save/current/" + sv.name + ".sv2";
+ name = FS.Gamedir() + "/save/current/" + SV_INIT.sv.name + ".sv2";
try {
- f= new QuakeFile(name, "rw");
+ f = new QuakeFile(name, "rw");
- for (int i= 0; i < MAX_CONFIGSTRINGS; i++)
- f.writeString(sv.configstrings[i]);
+ for (int i = 0; i < Defines.MAX_CONFIGSTRINGS; i++)
+ f.writeString(SV_INIT.sv.configstrings[i]);
CM.CM_WritePortalState(f);
f.close();
@@ -342,10 +353,9 @@ public class SV_CCMDS extends SV_ENTS {
e.printStackTrace();
}
- name= FS.Gamedir() + "/save/current/" + sv.name + ".sav";
+ name = FS.Gamedir() + "/save/current/" + SV_INIT.sv.name + ".sav";
GameSave.WriteLevel(name);
}
-
/*
==============
SV_ReadLevelFile
@@ -359,12 +369,12 @@ public class SV_CCMDS extends SV_ENTS {
Com.DPrintf("SV_ReadLevelFile()\n");
- name= FS.Gamedir() + "/save/current/" + sv.name + ".sv2";
+ name = FS.Gamedir() + "/save/current/" + SV_INIT.sv.name + ".sv2";
try {
- f= new QuakeFile(name, "r");
+ f = new QuakeFile(name, "r");
- for (int n= 0; n < MAX_CONFIGSTRINGS; n++)
- sv.configstrings[n]= f.readString();
+ for (int n = 0; n < Defines.MAX_CONFIGSTRINGS; n++)
+ SV_INIT.sv.configstrings[n] = f.readString();
CM.CM_ReadPortalState(f);
@@ -375,10 +385,9 @@ public class SV_CCMDS extends SV_ENTS {
e1.printStackTrace();
}
- name= FS.Gamedir() + "/save/current/" + sv.name + ".sav";
+ name = FS.Gamedir() + "/save/current/" + SV_INIT.sv.name + ".sav";
GameSave.ReadLevel(name);
}
-
/*
==============
SV_WriteServerFile
@@ -393,41 +402,42 @@ public class SV_CCMDS extends SV_ENTS {
Com.DPrintf("SV_WriteServerFile(" + (autosave ? "true" : "false") + ")\n");
- filename= FS.Gamedir() + "/save/current/server.ssv";
+ filename = FS.Gamedir() + "/save/current/server.ssv";
try {
- f= new QuakeFile(filename, "rw");
+ f = new QuakeFile(filename, "rw");
if (!autosave) {
- Calendar c= Calendar.getInstance();
- comment=
+ Calendar c = Calendar.getInstance();
+ comment =
Com.sprintf(
"%2i:%2i %2i/%2i ",
- new Vargs().add(c.get(Calendar.HOUR_OF_DAY)).add(c.get(Calendar.MINUTE)).add(c.get(Calendar.MONTH) + 1).add(
+ new Vargs().add(c.get(Calendar.HOUR_OF_DAY)).add(c.get(Calendar.MINUTE)).add(
+ c.get(Calendar.MONTH) + 1).add(
c.get(Calendar.DAY_OF_MONTH)));
- comment += sv.configstrings[CS_NAME];
+ comment += SV_INIT.sv.configstrings[Defines.CS_NAME];
}
else {
// autosaved
- comment= "ENTERING " + sv.configstrings[CS_NAME];
+ comment = "ENTERING " + SV_INIT.sv.configstrings[Defines.CS_NAME];
}
f.writeString(comment);
- f.writeString(svs.mapcmd);
+ f.writeString(SV_INIT.svs.mapcmd);
// write the mapcmd
// write all CVAR_LATCH cvars
// these will be things like coop, skill, deathmatch, etc
- for (var= Globals.cvar_vars; var != null; var= var.next) {
- if (0 == (var.flags & CVAR_LATCH))
+ for (var = Globals.cvar_vars; var != null; var = var.next) {
+ if (0 == (var.flags & Defines.CVAR_LATCH))
continue;
- if (var.name.length() >= MAX_OSPATH - 1 || var.string.length() >= 128 - 1) {
+ if (var.name.length() >= Defines.MAX_OSPATH - 1 || var.string.length() >= 128 - 1) {
Com.Printf("Cvar too long: " + var.name + " = " + var.string + "\n");
continue;
}
- name= var.name;
- string= var.string;
+ name = var.name;
+ string = var.string;
try {
f.writeString(name);
f.writeString(string);
@@ -445,10 +455,9 @@ public class SV_CCMDS extends SV_ENTS {
}
// write game state
- filename= FS.Gamedir() + "/save/current/game.ssv";
+ filename = FS.Gamedir() + "/save/current/game.ssv";
GameSave.WriteGame(filename, autosave);
}
-
/*
==============
SV_ReadServerFile
@@ -456,31 +465,31 @@ public class SV_CCMDS extends SV_ENTS {
==============
*/
public static void SV_ReadServerFile() {
- String filename, name= "", string, comment, mapcmd;
+ String filename, name = "", string, comment, mapcmd;
try {
QuakeFile f;
- mapcmd= "";
+ mapcmd = "";
Com.DPrintf("SV_ReadServerFile()\n");
- filename= FS.Gamedir() + "/save/current/server.ssv";
+ filename = FS.Gamedir() + "/save/current/server.ssv";
- f= new QuakeFile(filename, "r");
+ f = new QuakeFile(filename, "r");
// read the comment field
- comment= f.readString();
+ comment = f.readString();
// read the mapcmd
- mapcmd= f.readString();
+ mapcmd = f.readString();
// read all CVAR_LATCH cvars
// these will be things like coop, skill, deathmatch, etc
while (true) {
- name= f.readString();
+ name = f.readString();
if (name == null)
break;
- string= f.readString();
+ string = f.readString();
Com.DPrintf("Set " + name + " = " + string + "\n");
Cvar.ForceSet(name, string);
@@ -489,12 +498,12 @@ public class SV_CCMDS extends SV_ENTS {
f.close();
// start a new game fresh with new cvars
- SV_InitGame();
+ SV_INIT.SV_InitGame();
- svs.mapcmd= mapcmd;
+ SV_INIT.svs.mapcmd = mapcmd;
// read game state
- filename= FS.Gamedir() + "/save/current/game.ssv";
+ filename = FS.Gamedir() + "/save/current/game.ssv";
GameSave.ReadGame(filename);
}
catch (Exception e) {
@@ -502,7 +511,6 @@ public class SV_CCMDS extends SV_ENTS {
e.printStackTrace();
}
}
-
//=========================================================
/*
@@ -513,9 +521,8 @@ public class SV_CCMDS extends SV_ENTS {
==================
*/
public static void SV_DemoMap_f() {
- SV_Map(true, Cmd.Argv(1), false);
+ SV_INIT.SV_Map(true, Cmd.Argv(1), false);
}
-
/*
==================
SV_GameMap_f
@@ -550,48 +557,47 @@ public class SV_CCMDS extends SV_ENTS {
FS.CreatePath(FS.Gamedir() + "/save/current/");
// check for clearing the current savegame
- map= Cmd.Argv(1);
+ map = Cmd.Argv(1);
if (map.charAt(0) == '*') {
// wipe all the *.sav files
SV_WipeSavegame("current");
}
else { // save the map just exited
- if (sv.state == ss_game) {
+ if (SV_INIT.sv.state == Defines.ss_game) {
// clear all the client inuse flags before saving so that
// when the level is re-entered, the clients will spawn
// at spawn points instead of occupying body shells
- savedInuse= new boolean[(int) maxclients.value];
- for (i= 0; i < maxclients.value; i++) {
- cl= svs.clients[i];
- savedInuse[i]= cl.edict.inuse;
- cl.edict.inuse= false;
+ savedInuse = new boolean[(int) SV_MAIN.maxclients.value];
+ for (i = 0; i < SV_MAIN.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ savedInuse[i] = cl.edict.inuse;
+ cl.edict.inuse = false;
}
SV_WriteLevelFile();
// we must restore these for clients to transfer over correctly
- for (i= 0; i < maxclients.value; i++) {
- cl= svs.clients[i];
- cl.edict.inuse= savedInuse[i];
+ for (i = 0; i < SV_MAIN.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ cl.edict.inuse = savedInuse[i];
}
- savedInuse= null;
+ savedInuse = null;
}
}
// start up the next map
- SV_Map(false, Cmd.Argv(1), false);
+ SV_INIT.SV_Map(false, Cmd.Argv(1), false);
// archive server state
- svs.mapcmd= Cmd.Argv(1);
+ SV_INIT.svs.mapcmd = Cmd.Argv(1);
// copy off the level to the autosave slot
- if (0 == dedicated.value) {
+ if (0 == Globals.dedicated.value) {
SV_WriteServerFile(true);
SV_CopySaveGame("current", "save0");
}
}
-
/*
==================
SV_Map_f
@@ -606,9 +612,9 @@ public class SV_CCMDS extends SV_ENTS {
String expanded;
// if not a pcx, demo, or cinematic, check to make sure the level exists
- map= Cmd.Argv(1);
+ map = Cmd.Argv(1);
if (map.indexOf(".") < 0) {
- expanded= "maps/" + map + ".bsp";
+ expanded = "maps/" + map + ".bsp";
if (FS.LoadFile(expanded) == null) {
Com.Printf("Can't find " + expanded + "\n");
@@ -616,12 +622,11 @@ public class SV_CCMDS extends SV_ENTS {
}
}
- sv.state= ss_dead; // don't save current level when changing
+ SV_INIT.sv.state = Defines.ss_dead; // don't save current level when changing
SV_WipeSavegame("current");
SV_GameMap_f();
}
-
/*
=====================================================================
@@ -649,15 +654,15 @@ public class SV_CCMDS extends SV_ENTS {
Com.Printf("Loading game...\n");
- dir= Cmd.Argv(1);
- if (strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\")) {
+ dir = Cmd.Argv(1);
+ if (Lib.strstr(dir, "..") || Lib.strstr(dir, "/") || Lib.strstr(dir, "\\")) {
Com.Printf("Bad savedir.\n");
}
// make sure the server.ssv file exists
- name= FS.Gamedir() + "/save/" + Cmd.Argv(1) + "/server.ssv";
+ name = FS.Gamedir() + "/save/" + Cmd.Argv(1) + "/server.ssv";
try {
- f= new RandomAccessFile(name, "r");
+ f = new RandomAccessFile(name, "r");
}
catch (FileNotFoundException e) {
Com.Printf("No such savegame: " + name + "\n");
@@ -675,10 +680,9 @@ public class SV_CCMDS extends SV_ENTS {
SV_ReadServerFile();
// go to the map
- sv.state= ss_dead; // don't save current level when changing
- SV_INIT.SV_Map(false, svs.mapcmd, true);
+ SV_INIT.sv.state = Defines.ss_dead; // don't save current level when changing
+ SV_INIT.SV_Map(false, SV_INIT.svs.mapcmd, true);
}
-
/*
==============
SV_Savegame_f
@@ -688,7 +692,7 @@ public class SV_CCMDS extends SV_ENTS {
public static void SV_Savegame_f() {
String dir;
- if (sv.state != ss_game) {
+ if (SV_INIT.sv.state != Defines.ss_game) {
Com.Printf("You must be in a game to save.\n");
return;
}
@@ -703,18 +707,18 @@ public class SV_CCMDS extends SV_ENTS {
return;
}
- if (0 == strcmp(Cmd.Argv(1), "current")) {
+ if (0 == Lib.strcmp(Cmd.Argv(1), "current")) {
Com.Printf("Can't save to 'current'\n");
return;
}
- if (maxclients.value == 1 && svs.clients[0].edict.client.ps.stats[STAT_HEALTH] <= 0) {
+ if (SV_MAIN.maxclients.value == 1 && SV_INIT.svs.clients[0].edict.client.ps.stats[Defines.STAT_HEALTH] <= 0) {
Com.Printf("\nCan't savegame while dead!\n");
return;
}
- dir= Cmd.Argv(1);
- if (strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\")) {
+ dir = Cmd.Argv(1);
+ if (Lib.strstr(dir, "..") || Lib.strstr(dir, "/") || Lib.strstr(dir, "\\")) {
Com.Printf("Bad savedir.\n");
}
@@ -737,7 +741,6 @@ public class SV_CCMDS extends SV_ENTS {
SV_CopySaveGame("current", dir);
Com.Printf("Done.\n");
}
-
//===============================================================
/*
==================
@@ -747,7 +750,7 @@ public class SV_CCMDS extends SV_ENTS {
==================
*/
public static void SV_Kick_f() {
- if (!svs.initialized) {
+ if (!SV_INIT.svs.initialized) {
Com.Printf("No server running.\n");
return;
}
@@ -760,14 +763,13 @@ public class SV_CCMDS extends SV_ENTS {
if (!SV_SetPlayer())
return;
- SV_BroadcastPrintf(PRINT_HIGH, sv_client.name + " was kicked\n");
+ SV_SEND.SV_BroadcastPrintf(Defines.PRINT_HIGH, SV_MAIN.sv_client.name + " was kicked\n");
// print directly, because the dropped client won't get the
// SV_BroadcastPrintf message
- SV_ClientPrintf(sv_client, PRINT_HIGH, "You were kicked from the game\n");
- SV_DropClient(sv_client);
- sv_client.lastmessage= svs.realtime; // min case there is a funny zombie
+ SV_SEND.SV_ClientPrintf(SV_MAIN.sv_client, Defines.PRINT_HIGH, "You were kicked from the game\n");
+ SV_MAIN.SV_DropClient(SV_MAIN.sv_client);
+ SV_MAIN.sv_client.lastmessage = SV_INIT.svs.realtime; // min case there is a funny zombie
}
-
/*
================
SV_Status_f
@@ -778,42 +780,42 @@ public class SV_CCMDS extends SV_ENTS {
client_t cl;
String s;
int ping;
- if (svs.clients == null) {
+ if (SV_INIT.svs.clients == null) {
Com.Printf("No server running.\n");
return;
}
- Com.Printf("map : " + sv.name + "\n");
+ Com.Printf("map : " + SV_INIT.sv.name + "\n");
Com.Printf("num score ping name lastmsg address qport \n");
Com.Printf("--- ----- ---- --------------- ------- --------------------- ------\n");
- for (i= 0; i < maxclients.value; i++) {
- cl= svs.clients[i];
+ for (i = 0; i < SV_MAIN.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
if (0 == cl.state)
continue;
Com.Printf("%3i ", new Vargs().add(i));
- Com.Printf("%5i ", new Vargs().add(cl.edict.client.ps.stats[STAT_FRAGS]));
+ Com.Printf("%5i ", new Vargs().add(cl.edict.client.ps.stats[Defines.STAT_FRAGS]));
- if (cl.state == cs_connected)
+ if (cl.state == Defines.cs_connected)
Com.Printf("CNCT ");
- else if (cl.state == cs_zombie)
+ else if (cl.state == Defines.cs_zombie)
Com.Printf("ZMBI ");
else {
- ping= cl.ping < 9999 ? cl.ping : 9999;
+ ping = cl.ping < 9999 ? cl.ping : 9999;
Com.Printf("%4i ", new Vargs().add(ping));
}
Com.Printf("%s", new Vargs().add(cl.name));
- l= 16 - cl.name.length();
- for (j= 0; j < l; j++)
+ l = 16 - cl.name.length();
+ for (j = 0; j < l; j++)
Com.Printf(" ");
- Com.Printf("%7i ", new Vargs().add(svs.realtime - cl.lastmessage));
+ Com.Printf("%7i ", new Vargs().add(SV_INIT.svs.realtime - cl.lastmessage));
- s= NET.AdrToString(cl.netchan.remote_address);
+ s = NET.AdrToString(cl.netchan.remote_address);
Com.Printf(s);
- l= 22 - s.length();
- for (j= 0; j < l; j++)
+ l = 22 - s.length();
+ for (j = 0; j < l; j++)
Com.Printf(" ");
Com.Printf("%5i", new Vargs().add(cl.netchan.qport));
@@ -822,7 +824,6 @@ public class SV_CCMDS extends SV_ENTS {
}
Com.Printf("\n");
}
-
/*
==================
SV_ConSay_f
@@ -837,32 +838,30 @@ public class SV_CCMDS extends SV_ENTS {
if (Cmd.Argc() < 2)
return;
- text= "console: ";
- p= Cmd.Args();
+ text = "console: ";
+ p = Cmd.Args();
if (p.charAt(0) == '"') {
- p= p.substring(1, p.length() - 1);
+ p = p.substring(1, p.length() - 1);
}
text += p;
- for (j= 0; j < maxclients.value; j++) {
- client= svs.clients[j];
- if (client.state != cs_spawned)
+ for (j = 0; j < SV_MAIN.maxclients.value; j++) {
+ client = SV_INIT.svs.clients[j];
+ if (client.state != Defines.cs_spawned)
continue;
- SV_ClientPrintf(client, PRINT_CHAT, text + "\n");
+ SV_SEND.SV_ClientPrintf(client, Defines.PRINT_CHAT, text + "\n");
}
}
-
/*
==================
SV_Heartbeat_f
==================
*/
public static void SV_Heartbeat_f() {
- svs.last_heartbeat= -9999999;
+ SV_INIT.svs.last_heartbeat = -9999999;
}
-
/*
===========
SV_Serverinfo_f
@@ -874,7 +873,6 @@ public class SV_CCMDS extends SV_ENTS {
Com.Printf("Server info settings:\n");
Info.Print(Cvar.Serverinfo());
}
-
/*
===========
SV_DumpUser_f
@@ -893,10 +891,9 @@ public class SV_CCMDS extends SV_ENTS {
Com.Printf("userinfo\n");
Com.Printf("--------\n");
- Info.Print(sv_client.userinfo);
+ Info.Print(SV_MAIN.sv_client.userinfo);
}
-
/*
==============
SV_ServerRecord_f
@@ -908,8 +905,8 @@ public class SV_CCMDS extends SV_ENTS {
public static void SV_ServerRecord_f() {
//char name[MAX_OSPATH];
String name;
- byte buf_data[]= new byte[32768];
- sizebuf_t buf= new sizebuf_t();
+ byte buf_data[] = new byte[32768];
+ sizebuf_t buf = new sizebuf_t();
int len;
int i;
@@ -918,12 +915,12 @@ public class SV_CCMDS extends SV_ENTS {
return;
}
- if (svs.demofile != null) {
+ if (SV_INIT.svs.demofile != null) {
Com.Printf("Already recording.\n");
return;
}
- if (sv.state != ss_game) {
+ if (SV_INIT.sv.state != Defines.ss_game) {
Com.Printf("You must be in a level to record.\n");
return;
}
@@ -931,12 +928,12 @@ public class SV_CCMDS extends SV_ENTS {
//
// open the demo file
//
- name= FS.Gamedir() + "/demos/" + Cmd.Argv(1) + ".dm2";
+ name = FS.Gamedir() + "/demos/" + Cmd.Argv(1) + ".dm2";
Com.Printf("recording to " + name + ".\n");
FS.CreatePath(name);
try {
- svs.demofile= new RandomAccessFile(name, "rw");
+ SV_INIT.svs.demofile = new RandomAccessFile(name, "rw");
}
catch (Exception e) {
Com.Printf("ERROR: couldn't open.\n");
@@ -944,7 +941,7 @@ public class SV_CCMDS extends SV_ENTS {
}
// setup a buffer to catch all multicasts
- SZ.Init(svs.demo_multicast, svs.demo_multicast_buf, svs.demo_multicast_buf.length);
+ SZ.Init(SV_INIT.svs.demo_multicast, SV_INIT.svs.demo_multicast_buf, SV_INIT.svs.demo_multicast_buf.length);
//
// write a single giant fake message with all the startup info
@@ -956,31 +953,31 @@ public class SV_CCMDS extends SV_ENTS {
// to make sure the protocol is right, and to set the gamedir
//
// send the serverdata
- MSG.WriteByte(buf, svc_serverdata);
- MSG.WriteLong(buf, PROTOCOL_VERSION);
- MSG.WriteLong(buf, svs.spawncount);
+ MSG.WriteByte(buf, Defines.svc_serverdata);
+ MSG.WriteLong(buf, Defines.PROTOCOL_VERSION);
+ MSG.WriteLong(buf, SV_INIT.svs.spawncount);
// 2 means server demo
MSG.WriteByte(buf, 2); // demos are always attract loops
MSG.WriteString(buf, Cvar.VariableString("gamedir"));
MSG.WriteShort(buf, -1);
// send full levelname
- MSG.WriteString(buf, sv.configstrings[CS_NAME]);
+ MSG.WriteString(buf, SV_INIT.sv.configstrings[Defines.CS_NAME]);
- for (i= 0; i < MAX_CONFIGSTRINGS; i++)
- if (sv.configstrings[i].length() == 0) {
- MSG.WriteByte(buf, svc_configstring);
+ for (i = 0; i < Defines.MAX_CONFIGSTRINGS; i++)
+ if (SV_INIT.sv.configstrings[i].length() == 0) {
+ MSG.WriteByte(buf, Defines.svc_configstring);
MSG.WriteShort(buf, i);
- MSG.WriteString(buf, sv.configstrings[i]);
+ MSG.WriteString(buf, SV_INIT.sv.configstrings[i]);
}
// write it to the demo file
Com.DPrintf("signon message length: " + buf.cursize + "\n");
- len= EndianHandler.swapInt(buf.cursize);
+ len = EndianHandler.swapInt(buf.cursize);
//fwrite(len, 4, 1, svs.demofile);
//fwrite(buf.data, buf.cursize, 1, svs.demofile);
try {
- svs.demofile.writeInt(len);
- svs.demofile.write(buf.data,0, buf.cursize);
+ SV_INIT.svs.demofile.writeInt(len);
+ SV_INIT.svs.demofile.write(buf.data, 0, buf.cursize);
}
catch (IOException e1) {
// TODO: do quake2 error handling!
@@ -989,7 +986,6 @@ public class SV_CCMDS extends SV_ENTS {
// the rest of the demo file will be individual frames
}
-
/*
==============
SV_ServerStop_f
@@ -998,20 +994,19 @@ public class SV_CCMDS extends SV_ENTS {
==============
*/
public static void SV_ServerStop_f() {
- if (svs.demofile == null) {
+ if (SV_INIT.svs.demofile == null) {
Com.Printf("Not doing a serverrecord.\n");
return;
}
try {
- svs.demofile.close();
+ SV_INIT.svs.demofile.close();
}
catch (IOException e) {
e.printStackTrace();
}
- svs.demofile= null;
+ SV_INIT.svs.demofile = null;
Com.Printf("Recording completed.\n");
}
-
/*
===============
SV_KillServer_f
@@ -1021,12 +1016,11 @@ public class SV_CCMDS extends SV_ENTS {
===============
*/
public static void SV_KillServer_f() {
- if (!svs.initialized)
+ if (!SV_INIT.svs.initialized)
return;
- SV_Shutdown("Server was killed.\n", false);
+ SV_MAIN.SV_Shutdown("Server was killed.\n", false);
NET.Config(false); // close network sockets
}
-
/*
===============
SV_ServerCommand_f
@@ -1036,9 +1030,8 @@ public class SV_CCMDS extends SV_ENTS {
*/
public static void SV_ServerCommand_f() {
- Game.ServerCommand();
+ GameSVCmds.ServerCommand();
}
-
//===========================================================
/*
@@ -1094,7 +1087,7 @@ public class SV_CCMDS extends SV_ENTS {
}
});
- if (dedicated.value != 0)
+ if (Globals.dedicated.value != 0)
Cmd.AddCommand("say", new xcommand_t() {
public void execute() {
SV_ConSay_f();
@@ -1135,5 +1128,4 @@ public class SV_CCMDS extends SV_ENTS {
}
});
}
-
}
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
diff --git a/src/jake2/server/SV_GAME.java b/src/jake2/server/SV_GAME.java
index 844aacb..33f35b4 100644
--- a/src/jake2/server/SV_GAME.java
+++ b/src/jake2/server/SV_GAME.java
@@ -1,318 +1,313 @@
/*
-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 14.01.2004 by RST.
-// $Id: SV_GAME.java,v 1.7 2004-08-29 21:39:25 hzi Exp $
-
+// $Id: SV_GAME.java,v 1.8 2004-09-22 19:22:12 salomo Exp $
package jake2.server;
+import jake2.Defines;
+import jake2.Globals;
import jake2.game.*;
import jake2.qcommon.*;
-
-public class SV_GAME extends SV_INIT {
-
-
- /*
- ===============
- PF_Unicast
-
- Sends the contents of the mutlicast buffer to a single client
- ===============
- */
- public static void PF_Unicast(edict_t ent, boolean reliable) {
- int p;
- client_t client;
-
- if (ent == null)
- return;
-
- p = ent.index;
- if (p < 1 || p > SV_MAIN.maxclients.value)
- return;
-
- client = SV_INIT.svs.clients[p - 1];
-
- if (reliable)
- SZ.Write(client.netchan.message, SV_INIT.sv.multicast.data, SV_INIT.sv.multicast.cursize);
- else
- SZ.Write(client.datagram, sv.multicast.data, sv.multicast.cursize);
-
- SZ.Clear(sv.multicast);
- }
-
- /*
- ===============
- PF_dprintf
-
- Debug print to server console
- ===============
- */
- public static void PF_dprintf(String fmt) {
- Com.Printf(fmt);
- }
-
- /*
- ===============
- PF_cprintf
-
- Print to a single client
- ===============
- */
- public static void PF_cprintf(edict_t ent, int level, String fmt) {
-
- int n = 0;
-
- if (ent != null) {
- n = ent.index;
- if (n < 1 || n > SV_MAIN.maxclients.value)
- Com.Error(ERR_DROP, "cprintf to a non-client");
- }
-
- if (ent != null)
- SV_SEND.SV_ClientPrintf(svs.clients[n - 1], level, fmt);
- else
- Com.Printf(fmt);
- }
-
- /*
- ===============
- PF_centerprintf
-
- centerprint to a single client
- ===============
- */
- public static void PF_centerprintf(edict_t ent, String fmt) {
- int n;
-
- n = ent.index;
- if (n < 1 || n > SV_MAIN.maxclients.value)
- return; // Com_Error (ERR_DROP, "centerprintf to a non-client");
-
- MSG.WriteByte(sv.multicast, svc_centerprint);
- MSG.WriteString(sv.multicast, fmt);
- PF_Unicast(ent, true);
- }
-
- /*
- ===============
- PF_error
-
- Abort the server with a game error
- ===============
- */
- public static void PF_error(String fmt) {
- Com.Error(ERR_DROP, "Game Error: " + fmt);
- }
-
- public static void PF_error(int level, String fmt) {
- Com.Error(level, fmt);
- }
-
- /*
- =================
- PF_setmodel
-
- Also sets mins and maxs for inline bmodels
- =================
- */
- public static void PF_setmodel(edict_t ent, String name) {
- int i;
- cmodel_t mod;
-
- if (name == null)
- Com.Error(ERR_DROP, "PF_setmodel: NULL");
-
- i = SV_ModelIndex(name);
-
- ent.s.modelindex = i;
-
- // if it is an inline model, get the size information for it
- if (name.startsWith("*")) {
- mod = CM.InlineModel(name);
- VectorCopy(mod.mins, ent.mins);
- VectorCopy(mod.maxs, ent.maxs);
- SV_WORLD.SV_LinkEdict(ent);
- }
- }
-
- /*
- ===============
- PF_Configstring
-
- ===============
- */
- public static void PF_Configstring(int index, String val) {
- if (index < 0 || index >= MAX_CONFIGSTRINGS)
- Com.Error(ERR_DROP, "configstring: bad index " + index + "\n");
-
- if (val == null)
- val = "";
-
- // change the string in sv
- sv.configstrings[index] = val;
-
- if (sv.state != ss_loading) { // send the update to everyone
- SZ.Clear(sv.multicast);
- MSG.WriteChar(sv.multicast, svc_configstring);
- MSG.WriteShort(sv.multicast, index);
- MSG.WriteString(sv.multicast, val);
-
- SV_SEND.SV_Multicast(vec3_origin, MULTICAST_ALL_R);
- }
- }
-
- public static void PF_WriteChar(int c) {
- MSG.WriteChar(sv.multicast, c);
- }
- public static void PF_WriteByte(int c) {
- MSG.WriteByte(sv.multicast, c);
- }
- public static void PF_WriteShort(int c) {
- MSG.WriteShort(sv.multicast, c);
- }
- public static void PF_WriteLong(int c) {
- MSG.WriteLong(sv.multicast, c);
- }
- public static void PF_WriteFloat(float f) {
- MSG.WriteFloat(sv.multicast, f);
- }
- public static void PF_WriteString(String s) {
- MSG.WriteString(sv.multicast, s);
- }
- public static void PF_WritePos(float[] pos) {
- MSG.WritePos(sv.multicast, pos);
- }
- public static void PF_WriteDir(float[] dir) {
- MSG.WriteDir(sv.multicast, dir);
- }
- public static void PF_WriteAngle(float f) {
- MSG.WriteAngle(sv.multicast, f);
- }
-
- /*
- =================
- PF_inPVS
-
- Also checks portalareas so that doors block sight
- =================
- */
- public static boolean PF_inPVS(float[] p1, float[] p2) {
- int leafnum;
- int cluster;
- int area1, area2;
- byte mask[];
-
- leafnum = CM.CM_PointLeafnum(p1);
- cluster = CM.CM_LeafCluster(leafnum);
- area1 = CM.CM_LeafArea(leafnum);
- mask = CM.CM_ClusterPVS(cluster);
-
- leafnum = CM.CM_PointLeafnum(p2);
- cluster = CM.CM_LeafCluster(leafnum);
- area2 = CM.CM_LeafArea(leafnum);
-
- // quake2 bugfix
- if (cluster == -1)
- return false;
- if (mask != null && (0 == (mask[cluster >>> 3] & (1 << (cluster & 7)))))
- return false;
-
- if (!CM.CM_AreasConnected(area1, area2))
- return false; // a door blocks sight
-
- return true;
- }
-
- /*
- =================
- PF_inPHS
-
- Also checks portalareas so that doors block sound
- =================
- */
- public static boolean PF_inPHS(float[] p1, float[] p2) {
- int leafnum;
- int cluster;
- int area1, area2;
- byte mask[];
-
- leafnum = CM.CM_PointLeafnum(p1);
- cluster = CM.CM_LeafCluster(leafnum);
- area1 = CM.CM_LeafArea(leafnum);
- mask = CM.CM_ClusterPHS(cluster);
-
- leafnum = CM.CM_PointLeafnum(p2);
- cluster = CM.CM_LeafCluster(leafnum);
- area2 = CM.CM_LeafArea(leafnum);
-
- // quake2 bugfix
- if (cluster == -1)
- return false;
- if (mask != null && (0 == (mask[cluster >> 3] & (1 << (cluster & 7)))))
- return false; // more than one bounce away
- if (!CM.CM_AreasConnected(area1, area2))
- return false; // a door blocks hearing
-
- return true;
- }
-
- public static void PF_StartSound(edict_t entity, int channel, int sound_num, float volume, float attenuation, float timeofs) {
-
- if (null == entity)
- return;
- SV_SEND.SV_StartSound (null, entity, channel, sound_num, volume, attenuation, timeofs);
-
- }
-
- //==============================================
-
- /*
- ===============
- SV_ShutdownGameProgs
-
- Called when either the entire server is being killed, or
- it is changing to a different game directory.
- ===============
- */
- public static void SV_ShutdownGameProgs() {
- Game.ShutdownGame();
- }
-
- /*
- ===============
- SV_InitGameProgs
-
- Init the game subsystem for a new map
- ===============
- */
-
- public static void SV_InitGameProgs() {
-
- // unload anything we have now
- SV_ShutdownGameProgs();
-
- game_import_t gimport = new game_import_t();
-
- // all functions set in game_export_t (rst)
- GameBase.GetGameApi(gimport);
-
- Game.InitGame();
- }
-}
+import jake2.util.Math3D;
+
+public class SV_GAME {
+
+ /*
+ * =============== PF_Unicast
+ *
+ * Sends the contents of the mutlicast buffer to a single client
+ * ===============
+ */
+ public static void PF_Unicast(edict_t ent, boolean reliable) {
+ int p;
+ client_t client;
+
+ if (ent == null)
+ return;
+
+ p = ent.index;
+ if (p < 1 || p > SV_MAIN.maxclients.value)
+ return;
+
+ client = SV_INIT.svs.clients[p - 1];
+
+ if (reliable)
+ SZ.Write(client.netchan.message, SV_INIT.sv.multicast.data,
+ SV_INIT.sv.multicast.cursize);
+ else
+ SZ.Write(client.datagram, SV_INIT.sv.multicast.data,
+ SV_INIT.sv.multicast.cursize);
+
+ SZ.Clear(SV_INIT.sv.multicast);
+ }
+
+ /*
+ * =============== PF_dprintf
+ *
+ * Debug print to server console ===============
+ */
+ public static void PF_dprintf(String fmt) {
+ Com.Printf(fmt);
+ }
+
+ /*
+ * =============== PF_cprintf
+ *
+ * Print to a single client ===============
+ */
+ public static void PF_cprintf(edict_t ent, int level, String fmt) {
+
+ int n = 0;
+
+ if (ent != null) {
+ n = ent.index;
+ if (n < 1 || n > SV_MAIN.maxclients.value)
+ Com.Error(Defines.ERR_DROP, "cprintf to a non-client");
+ }
+
+ if (ent != null)
+ SV_SEND.SV_ClientPrintf(SV_INIT.svs.clients[n - 1], level, fmt);
+ else
+ Com.Printf(fmt);
+ }
+
+ /*
+ * =============== PF_centerprintf
+ *
+ * centerprint to a single client ===============
+ */
+ public static void PF_centerprintf(edict_t ent, String fmt) {
+ int n;
+
+ n = ent.index;
+ if (n < 1 || n > SV_MAIN.maxclients.value)
+ return; // Com_Error (ERR_DROP, "centerprintf to a non-client");
+
+ MSG.WriteByte(SV_INIT.sv.multicast, Defines.svc_centerprint);
+ MSG.WriteString(SV_INIT.sv.multicast, fmt);
+ PF_Unicast(ent, true);
+ }
+
+ /*
+ * =============== PF_error
+ *
+ * Abort the server with a game error ===============
+ */
+ public static void PF_error(String fmt) {
+ Com.Error(Defines.ERR_DROP, "Game Error: " + fmt);
+ }
+
+ public static void PF_error(int level, String fmt) {
+ Com.Error(level, fmt);
+ }
+
+ /*
+ * ================= PF_setmodel
+ *
+ * Also sets mins and maxs for inline bmodels =================
+ */
+ public static void PF_setmodel(edict_t ent, String name) {
+ int i;
+ cmodel_t mod;
+
+ if (name == null)
+ Com.Error(Defines.ERR_DROP, "PF_setmodel: NULL");
+
+ i = SV_INIT.SV_ModelIndex(name);
+
+ ent.s.modelindex = i;
+
+ // if it is an inline model, get the size information for it
+ if (name.startsWith("*")) {
+ mod = CM.InlineModel(name);
+ Math3D.VectorCopy(mod.mins, ent.mins);
+ Math3D.VectorCopy(mod.maxs, ent.maxs);
+ SV_WORLD.SV_LinkEdict(ent);
+ }
+ }
+
+ /*
+ * =============== PF_Configstring
+ *
+ * ===============
+ */
+ public static void PF_Configstring(int index, String val) {
+ if (index < 0 || index >= Defines.MAX_CONFIGSTRINGS)
+ Com.Error(Defines.ERR_DROP, "configstring: bad index " + index
+ + "\n");
+
+ if (val == null)
+ val = "";
+
+ // change the string in sv
+ SV_INIT.sv.configstrings[index] = val;
+
+ if (SV_INIT.sv.state != Defines.ss_loading) { // send the update to
+ // everyone
+ SZ.Clear(SV_INIT.sv.multicast);
+ MSG.WriteChar(SV_INIT.sv.multicast, Defines.svc_configstring);
+ MSG.WriteShort(SV_INIT.sv.multicast, index);
+ MSG.WriteString(SV_INIT.sv.multicast, val);
+
+ SV_SEND.SV_Multicast(Globals.vec3_origin, Defines.MULTICAST_ALL_R);
+ }
+ }
+
+ public static void PF_WriteChar(int c) {
+ MSG.WriteChar(SV_INIT.sv.multicast, c);
+ }
+
+ public static void PF_WriteByte(int c) {
+ MSG.WriteByte(SV_INIT.sv.multicast, c);
+ }
+
+ public static void PF_WriteShort(int c) {
+ MSG.WriteShort(SV_INIT.sv.multicast, c);
+ }
+
+ public static void PF_WriteLong(int c) {
+ MSG.WriteLong(SV_INIT.sv.multicast, c);
+ }
+
+ public static void PF_WriteFloat(float f) {
+ MSG.WriteFloat(SV_INIT.sv.multicast, f);
+ }
+
+ public static void PF_WriteString(String s) {
+ MSG.WriteString(SV_INIT.sv.multicast, s);
+ }
+
+ public static void PF_WritePos(float[] pos) {
+ MSG.WritePos(SV_INIT.sv.multicast, pos);
+ }
+
+ public static void PF_WriteDir(float[] dir) {
+ MSG.WriteDir(SV_INIT.sv.multicast, dir);
+ }
+
+ public static void PF_WriteAngle(float f) {
+ MSG.WriteAngle(SV_INIT.sv.multicast, f);
+ }
+
+ /*
+ * ================= PF_inPVS
+ *
+ * Also checks portalareas so that doors block sight =================
+ */
+ public static boolean PF_inPVS(float[] p1, float[] p2) {
+ int leafnum;
+ int cluster;
+ int area1, area2;
+ byte mask[];
+
+ leafnum = CM.CM_PointLeafnum(p1);
+ cluster = CM.CM_LeafCluster(leafnum);
+ area1 = CM.CM_LeafArea(leafnum);
+ mask = CM.CM_ClusterPVS(cluster);
+
+ leafnum = CM.CM_PointLeafnum(p2);
+ cluster = CM.CM_LeafCluster(leafnum);
+ area2 = CM.CM_LeafArea(leafnum);
+
+ // quake2 bugfix
+ if (cluster == -1)
+ return false;
+ if (mask != null && (0 == (mask[cluster >>> 3] & (1 << (cluster & 7)))))
+ return false;
+
+ if (!CM.CM_AreasConnected(area1, area2))
+ return false; // a door blocks sight
+
+ return true;
+ }
+
+ /*
+ * ================= PF_inPHS
+ *
+ * Also checks portalareas so that doors block sound =================
+ */
+ public static boolean PF_inPHS(float[] p1, float[] p2) {
+ int leafnum;
+ int cluster;
+ int area1, area2;
+ byte mask[];
+
+ leafnum = CM.CM_PointLeafnum(p1);
+ cluster = CM.CM_LeafCluster(leafnum);
+ area1 = CM.CM_LeafArea(leafnum);
+ mask = CM.CM_ClusterPHS(cluster);
+
+ leafnum = CM.CM_PointLeafnum(p2);
+ cluster = CM.CM_LeafCluster(leafnum);
+ area2 = CM.CM_LeafArea(leafnum);
+
+ // quake2 bugfix
+ if (cluster == -1)
+ return false;
+ if (mask != null && (0 == (mask[cluster >> 3] & (1 << (cluster & 7)))))
+ return false; // more than one bounce away
+ if (!CM.CM_AreasConnected(area1, area2))
+ return false; // a door blocks hearing
+
+ return true;
+ }
+
+ public static void PF_StartSound(edict_t entity, int channel,
+ int sound_num, float volume, float attenuation, float timeofs) {
+
+ if (null == entity)
+ return;
+ SV_SEND.SV_StartSound(null, entity, channel, sound_num, volume,
+ attenuation, timeofs);
+
+ }
+
+ //==============================================
+
+ /*
+ * =============== SV_ShutdownGameProgs
+ *
+ * Called when either the entire server is being killed, or it is changing
+ * to a different game directory. ===============
+ */
+ public static void SV_ShutdownGameProgs() {
+ GameBase.ShutdownGame();
+ }
+
+ /*
+ * =============== SV_InitGameProgs
+ *
+ * Init the game subsystem for a new map ===============
+ */
+
+ public static void SV_InitGameProgs() {
+
+ // unload anything we have now
+ SV_ShutdownGameProgs();
+
+ game_import_t gimport = new game_import_t();
+
+ // all functions set in game_export_t (rst)
+ GameBase.GetGameApi(gimport);
+
+ GameSave.InitGame();
+ }
+} \ No newline at end of file
diff --git a/src/jake2/server/SV_INIT.java b/src/jake2/server/SV_INIT.java
index 2325f22..0c1c5ae 100644
--- a/src/jake2/server/SV_INIT.java
+++ b/src/jake2/server/SV_INIT.java
@@ -1,493 +1,499 @@
/*
-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 14.01.2004 by RST.
-// $Id: SV_INIT.java,v 1.8 2004-08-29 21:39:25 hzi Exp $
-
+// $Id: SV_INIT.java,v 1.9 2004-09-22 19:22:12 salomo Exp $
package jake2.server;
+import jake2.Defines;
import jake2.Globals;
import jake2.client.CL;
import jake2.client.SCR;
import jake2.game.*;
import jake2.qcommon.*;
import jake2.sys.NET;
+import jake2.util.Lib;
+import jake2.util.Math3D;
import java.io.IOException;
import java.io.RandomAccessFile;
-public class SV_INIT extends Globals {
-
- public static server_static_t svs= new server_static_t(); // persistant server info
- public static server_t sv= new server_t(); // local server
-
- /*
- ================
- SV_FindIndex
-
- ================
- */
- public static int SV_FindIndex(String name, int start, int max, boolean create) {
- int i;
-
- if (name == null || name.length() == 0)
- return 0;
-
- for (i= 1; i < max && sv.configstrings[start + i] != null; i++)
- if (0 == strcmp(sv.configstrings[start + i], name))
- return i;
-
- if (!create)
- return 0;
-
- if (i == max)
- Com.Error(ERR_DROP, "*Index: overflow");
-
- //strncpy (sv.configstrings[start+i], name, sizeof(sv.configstrings[i]));
- sv.configstrings[start + i]= name;
-
- if (sv.state != ss_loading) { // send the update to everyone
- SZ.Clear(sv.multicast);
- MSG.WriteChar(sv.multicast, svc_configstring);
- MSG.WriteShort(sv.multicast, start + i);
- MSG.WriteString(sv.multicast, name);
- SV_SEND.SV_Multicast(Game.vec3_origin, MULTICAST_ALL_R);
- }
-
- return i;
- }
-
- public static int SV_ModelIndex(String name) {
- return SV_FindIndex(name, CS_MODELS, MAX_MODELS, true);
- }
-
- public static int SV_SoundIndex(String name) {
- return SV_FindIndex(name, CS_SOUNDS, MAX_SOUNDS, true);
- }
-
- public static int SV_ImageIndex(String name) {
- return SV_FindIndex(name, CS_IMAGES, MAX_IMAGES, true);
- }
-
- /*
- ================
- SV_CreateBaseline
-
- Entity baselines are used to compress the update messages
- to the clients -- only the fields that differ from the
- baseline will be transmitted
- ================
- */
- public static void SV_CreateBaseline() {
- edict_t svent;
- int entnum;
-
- for (entnum= 1; entnum < GameBase.num_edicts; entnum++) {
- //svent = EDICT_NUM(entnum);
- svent= GameBase.g_edicts[entnum];
-
- if (!svent.inuse)
- continue;
- if (0 == svent.s.modelindex && 0 == svent.s.sound && 0 == svent.s.effects)
- continue;
- svent.s.number= entnum;
-
- //
- // take current state as baseline
- //
- VectorCopy(svent.s.origin, svent.s.old_origin);
- // rst: bugfix
- sv.baselines[entnum].set(svent.s); // = svent.s.getClone();
- }
- }
-
- /*
- =================
- SV_CheckForSavegame
- =================
- */
- public static void SV_CheckForSavegame() {
-
- String name;
- RandomAccessFile f;
-
- int i;
-
- if (SV_MAIN.sv_noreload.value != 0)
- return;
-
- if (Cvar.VariableValue("deathmatch") != 0)
- return;
-
- name= FS.Gamedir() + "/save/current/" + sv.name + ".sav";
- try {
- f= new RandomAccessFile(name, "r");
- }
-
- catch (Exception e) {
- return;
- }
-
- try {
- f.close();
- }
- catch (IOException e1) {
- e1.printStackTrace();
- }
-
- SV_WORLD.SV_ClearWorld();
-
- // get configstrings and areaportals
- SV_CCMDS.SV_ReadLevelFile();
-
- if (!sv.loadgame) { // coming back to a level after being in a different
- // level, so run it for ten seconds
-
- // rlava2 was sending too many lightstyles, and overflowing the
- // reliable data. temporarily changing the server state to loading
- // prevents these from being passed down.
- int previousState; // PGM
-
- previousState= sv.state; // PGM
- sv.state= ss_loading; // PGM
- for (i= 0; i < 100; i++)
- Game.G_RunFrame();
-
- sv.state= previousState; // PGM
- }
- }
-
- /*
- ================
- SV_SpawnServer
-
- Change the server to a new map, taking all connected
- clients along with it.
-
- ================
- */
- public static void SV_SpawnServer(String server, String spawnpoint, int serverstate, boolean attractloop, boolean loadgame) {
- int i;
- int checksum= 0;
-
- if (attractloop)
- Cvar.Set("paused", "0");
-
- Com.Printf("------- Server Initialization -------\n");
-
- Com.DPrintf("SpawnServer: " + server + "\n");
- if (sv.demofile != null)
- try {
- sv.demofile.close();
- }
- catch (Exception e) {
- }
-
- svs.spawncount++; // any partially connected client will be
- // restarted
-
- sv.state= ss_dead;
-
- Globals.server_state= sv.state;
-
- // wipe the entire per-level structure
- //memset(sv, 0, sizeof(sv));
- sv= new server_t();
-
- svs.realtime= 0;
- sv.loadgame= loadgame;
- sv.attractloop= attractloop;
-
- // save name for levels that don't set message
- sv.configstrings[CS_NAME]= server;
-
- if (Cvar.VariableValue("deathmatch") != 0) {
- sv.configstrings[CS_AIRACCEL]= "" + SV_MAIN.sv_airaccelerate.value;
- PMove.pm_airaccelerate= SV_MAIN.sv_airaccelerate.value;
- }
- else {
- sv.configstrings[CS_AIRACCEL]= "0";
- PMove.pm_airaccelerate= 0;
- }
-
- SZ.Init(sv.multicast, sv.multicast_buf, sv.multicast_buf.length);
-
- sv.name= server;
-
- // leave slots at start for clients only
- for (i= 0; i < SV_MAIN.maxclients.value; i++) {
- // needs to reconnect
- if (svs.clients[i].state > cs_connected)
- svs.clients[i].state= cs_connected;
- svs.clients[i].lastframe= -1;
- }
-
- sv.time= 1000;
-
- sv.name= server;
- sv.configstrings[CS_NAME]= server;
-
- int iw[]= { checksum };
-
- if (serverstate != ss_game) {
- sv.models[1]= CM.CM_LoadMap("", false, iw); // no real map
- }
- else {
- sv.configstrings[CS_MODELS + 1]= "maps/" + server + ".bsp";
- sv.models[1]= CM.CM_LoadMap(sv.configstrings[CS_MODELS + 1], false, iw);
- }
- checksum= iw[0];
- sv.configstrings[CS_MAPCHECKSUM]= "" + checksum;
-
- //
- // clear physics interaction links
- //
- SV_WORLD.SV_ClearWorld();
-
- for (i= 1; i < CM.CM_NumInlineModels(); i++) {
- sv.configstrings[CS_MODELS + 1 + i]= "*" + i;
- // copy references
- sv.models[i + 1]= CM.InlineModel(sv.configstrings[CS_MODELS + 1 + i]);
- }
-
- //
- // spawn the rest of the entities on the map
- //
-
- // precache and static commands can be issued during
- // map initialization
-
- sv.state= ss_loading;
- Globals.server_state= sv.state;
-
- // load and spawn all other entities
- Game.SpawnEntities(sv.name, CM.CM_EntityString(), spawnpoint);
-
- // run two frames to allow everything to settle
- Game.G_RunFrame();
- Game.G_RunFrame();
-
- // all precaches are complete
- sv.state= serverstate;
- Globals.server_state= sv.state;
-
- // create a baseline for more efficient communications
- SV_CreateBaseline();
-
- // check for a savegame
- SV_CheckForSavegame();
-
- // set serverinfo variable
- Cvar.FullSet("mapname", sv.name, CVAR_SERVERINFO | CVAR_NOSET);
-
- //Com.Printf("-------------------------------------\n");
- }
-
- /*
- ==============
- SV_InitGame
-
- A brand new game has been started
- ==============
- */
- public static void SV_InitGame() {
- int i;
- edict_t ent;
- //char idmaster[32];
- String idmaster;
-
- if (svs.initialized) {
- // cause any connected clients to reconnect
- SV_MAIN.SV_Shutdown("Server restarted\n", true);
- }
- else {
- // make sure the client is down
- CL.Drop();
- SCR.BeginLoadingPlaque();
- }
-
- // get any latched variable changes (maxclients, etc)
- Cvar.GetLatchedVars();
-
- svs.initialized= true;
-
- if (Cvar.VariableValue("coop") != 0 && Cvar.VariableValue("deathmatch") != 0) {
- Com.Printf("Deathmatch and Coop both set, disabling Coop\n");
- Cvar.FullSet("coop", "0", CVAR_SERVERINFO | CVAR_LATCH);
- }
-
- // dedicated servers are can't be single player and are usually DM
- // so unless they explicity set coop, force it to deathmatch
- if (dedicated.value != 0) {
- if (0 == Cvar.VariableValue("coop"))
- Cvar.FullSet("deathmatch", "1", CVAR_SERVERINFO | CVAR_LATCH);
- }
-
- // init clients
- if (Cvar.VariableValue("deathmatch") != 0) {
- if (SV_MAIN.maxclients.value <= 1)
- Cvar.FullSet("maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH);
- else if (SV_MAIN.maxclients.value > MAX_CLIENTS)
- Cvar.FullSet("maxclients", "" + MAX_CLIENTS, CVAR_SERVERINFO | CVAR_LATCH);
- }
- else if (Cvar.VariableValue("coop") != 0) {
- if (SV_MAIN.maxclients.value <= 1 || SV_MAIN.maxclients.value > 4)
- Cvar.FullSet("maxclients", "4", CVAR_SERVERINFO | CVAR_LATCH);
-
- }
- else // non-deathmatch, non-coop is one player
- {
- Cvar.FullSet("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH);
- }
-
- svs.spawncount= rand();
- //svs.clients = Z_Malloc(sizeof(client_t) * maxclients.value);
- svs.clients= new client_t[(int) SV_MAIN.maxclients.value];
- for (int n= 0; n < svs.clients.length; n++)
- svs.clients[n]= new client_t();
-
- svs.num_client_entities= ((int) SV_MAIN.maxclients.value) * UPDATE_BACKUP * 64; //ok.
-
- //svs.client_entities = Z_Malloc(sizeof(entity_state_t) * svs.num_client_entities);
- svs.client_entities= new entity_state_t[svs.num_client_entities];
- for (int n= 0; n < svs.client_entities.length; n++)
- svs.client_entities[n]= new entity_state_t(null);
-
- // init network stuff
- NET.Config((SV_MAIN.maxclients.value > 1)); //ok!
-
- // heartbeats will always be sent to the id master
- svs.last_heartbeat= -99999; // send immediately
- idmaster= "192.246.40.37:" + PORT_MASTER;
- NET.StringToAdr(idmaster, SV_MAIN.master_adr[0]);
-
- // init game
- SV_GAME.SV_InitGameProgs(); // bis hier alles ok!
-
- for (i= 0; i < SV_MAIN.maxclients.value; i++) {
- ent= GameBase.g_edicts[i + 1];
-
- //ent.s.number = i + 1; //dont need this, ent.s.number already set.
- svs.clients[i].edict= ent;
- //memset(& svs.clients[i].lastcmd, 0, sizeof(svs.clients[i].lastcmd));
- svs.clients[i].lastcmd= new usercmd_t();
- }
- }
-
- /*
- ======================
- SV_Map
-
- the full syntax is:
-
- map [*]<map>$<startspot>+<nextserver>
-
- command from the console or progs.
- Map can also be a.cin, .pcx, or .dm2 file
- Nextserver is used to allow a cinematic to play, then proceed to
- another level:
-
- map tram.cin+jail_e3
- ======================
- */
- public static void SV_Map(boolean attractloop, String levelstring, boolean loadgame) {
- //char level[MAX_QPATH];
- //char *ch;
- int l;
- //char spawnpoint[MAX_QPATH];
-
- String level, ch, spawnpoint;
-
- sv.loadgame= loadgame;
- sv.attractloop= attractloop;
-
- if (sv.state == ss_dead && !sv.loadgame)
- SV_InitGame(); // the game is just starting
-
- level= levelstring; // bis hier her ok.
-
- // if there is a + in the map, set nextserver to the remainder
-
- //was:
- // ch = strstr(level, "+");
- // if (ch)
- // {
- // *ch = 0;
- // Cvar_Set ("nextserver", va("gamemap \"%s\"", ch+1));
- // }
- // else
- // Cvar_Set ("nextserver", "");
-
- int c= level.indexOf('+');
- if (c != -1) {
- Cvar.Set("nextserver", "gamemap \"" + level.substring(c + 1) + "\"");
- level= level.substring(0, c);
- }
- else {
- Cvar.Set("nextserver", "");
- }
-
- //ZOID special hack for end game screen in coop mode
- if (Cvar.VariableValue("coop") != 0 && !level.equals("victory.pcx"))
- Cvar.Set("nextserver", "gamemap \"*base1\"");
-
- // if there is a $, use the remainder as a spawnpoint
- int pos= level.indexOf('$');
- if (pos != -1) {
- //* ch = 0;
- spawnpoint= level.substring(pos + 1);
- level= level.substring(0, pos);
-
- }
- else
- //spawnpoint[0] = 0;
- spawnpoint= "";
-
- // skip the end-of-unit flag if necessary
- if (level.charAt(0) == '*')
- level= level.substring(1);
-
- l= level.length();
- if (l > 4 && level.endsWith(".cin")) {
- SCR.BeginLoadingPlaque(); // for local system
- SV_SEND.SV_BroadcastCommand("changing\n");
- SV_SpawnServer(level, spawnpoint, ss_cinematic, attractloop, loadgame);
- }
- else if (l > 4 && level.endsWith(".dm2")) {
- SCR.BeginLoadingPlaque(); // for local system
- SV_SEND.SV_BroadcastCommand("changing\n");
- SV_SpawnServer(level, spawnpoint, ss_demo, attractloop, loadgame);
- }
- else if (l > 4 && level.endsWith(".pcx")) {
- SCR.BeginLoadingPlaque(); // for local system
- SV_SEND.SV_BroadcastCommand("changing\n");
- SV_SpawnServer(level, spawnpoint, ss_pic, attractloop, loadgame);
- }
- else {
- SCR.BeginLoadingPlaque(); // for local system
- SV_SEND.SV_BroadcastCommand("changing\n");
- SV_SEND.SV_SendClientMessages();
- SV_SpawnServer(level, spawnpoint, ss_game, attractloop, loadgame);
- Cbuf.CopyToDefer();
- }
-
- SV_SEND.SV_BroadcastCommand("reconnect\n");
- }
-}
+public class SV_INIT {
+
+ /*
+ * ================ SV_FindIndex
+ *
+ * ================
+ */
+ public static int SV_FindIndex(String name, int start, int max,
+ boolean create) {
+ int i;
+
+ if (name == null || name.length() == 0)
+ return 0;
+
+ for (i = 1; i < max && sv.configstrings[start + i] != null; i++)
+ if (0 == Lib.strcmp(sv.configstrings[start + i], name))
+ return i;
+
+ if (!create)
+ return 0;
+
+ if (i == max)
+ Com.Error(Defines.ERR_DROP, "*Index: overflow");
+
+ //strncpy (sv.configstrings[start+i], name,
+ // sizeof(sv.configstrings[i]));
+ sv.configstrings[start + i] = name;
+
+ if (sv.state != Defines.ss_loading) { // send the update to everyone
+ SZ.Clear(sv.multicast);
+ MSG.WriteChar(sv.multicast, Defines.svc_configstring);
+ MSG.WriteShort(sv.multicast, start + i);
+ MSG.WriteString(sv.multicast, name);
+ SV_SEND.SV_Multicast(Globals.vec3_origin, Defines.MULTICAST_ALL_R);
+ }
+
+ return i;
+ }
+
+ public static int SV_ModelIndex(String name) {
+ return SV_FindIndex(name, Defines.CS_MODELS, Defines.MAX_MODELS, true);
+ }
+
+ public static int SV_SoundIndex(String name) {
+ return SV_FindIndex(name, Defines.CS_SOUNDS, Defines.MAX_SOUNDS, true);
+ }
+
+ public static int SV_ImageIndex(String name) {
+ return SV_FindIndex(name, Defines.CS_IMAGES, Defines.MAX_IMAGES, true);
+ }
+
+ /*
+ * ================ SV_CreateBaseline
+ *
+ * Entity baselines are used to compress the update messages to the clients --
+ * only the fields that differ from the baseline will be transmitted
+ * ================
+ */
+ public static void SV_CreateBaseline() {
+ edict_t svent;
+ int entnum;
+
+ for (entnum = 1; entnum < GameBase.num_edicts; entnum++) {
+ //svent = EDICT_NUM(entnum);
+ svent = GameBase.g_edicts[entnum];
+
+ if (!svent.inuse)
+ continue;
+ if (0 == svent.s.modelindex && 0 == svent.s.sound
+ && 0 == svent.s.effects)
+ continue;
+ svent.s.number = entnum;
+
+ //
+ // take current state as baseline
+ //
+ Math3D.VectorCopy(svent.s.origin, svent.s.old_origin);
+ // rst: bugfix
+ sv.baselines[entnum].set(svent.s); // = svent.s.getClone();
+ }
+ }
+
+ /*
+ * ================= SV_CheckForSavegame =================
+ */
+ public static void SV_CheckForSavegame() {
+
+ String name;
+ RandomAccessFile f;
+
+ int i;
+
+ if (SV_MAIN.sv_noreload.value != 0)
+ return;
+
+ if (Cvar.VariableValue("deathmatch") != 0)
+ return;
+
+ name = FS.Gamedir() + "/save/current/" + sv.name + ".sav";
+ try {
+ f = new RandomAccessFile(name, "r");
+ }
+
+ catch (Exception e) {
+ return;
+ }
+
+ try {
+ f.close();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+
+ SV_WORLD.SV_ClearWorld();
+
+ // get configstrings and areaportals
+ SV_CCMDS.SV_ReadLevelFile();
+
+ if (!sv.loadgame) { // coming back to a level after being in a different
+ // level, so run it for ten seconds
+
+ // rlava2 was sending too many lightstyles, and overflowing the
+ // reliable data. temporarily changing the server state to loading
+ // prevents these from being passed down.
+ int previousState; // PGM
+
+ previousState = sv.state; // PGM
+ sv.state = Defines.ss_loading; // PGM
+ for (i = 0; i < 100; i++)
+ GameBase.G_RunFrame();
+
+ sv.state = previousState; // PGM
+ }
+ }
+
+ /*
+ * ================ SV_SpawnServer
+ *
+ * Change the server to a new map, taking all connected clients along with
+ * it.
+ *
+ * ================
+ */
+ public static void SV_SpawnServer(String server, String spawnpoint,
+ int serverstate, boolean attractloop, boolean loadgame) {
+ int i;
+ int checksum = 0;
+
+ if (attractloop)
+ Cvar.Set("paused", "0");
+
+ Com.Printf("------- Server Initialization -------\n");
+
+ Com.DPrintf("SpawnServer: " + server + "\n");
+ if (sv.demofile != null)
+ try {
+ sv.demofile.close();
+ } catch (Exception e) {
+ }
+
+ svs.spawncount++; // any partially connected client will be
+ // restarted
+
+ sv.state = Defines.ss_dead;
+
+ Globals.server_state = sv.state;
+
+ // wipe the entire per-level structure
+ //memset(sv, 0, sizeof(sv));
+ sv = new server_t();
+
+ svs.realtime = 0;
+ sv.loadgame = loadgame;
+ sv.attractloop = attractloop;
+
+ // save name for levels that don't set message
+ sv.configstrings[Defines.CS_NAME] = server;
+
+ if (Cvar.VariableValue("deathmatch") != 0) {
+ sv.configstrings[Defines.CS_AIRACCEL] = ""
+ + SV_MAIN.sv_airaccelerate.value;
+ PMove.pm_airaccelerate = SV_MAIN.sv_airaccelerate.value;
+ } else {
+ sv.configstrings[Defines.CS_AIRACCEL] = "0";
+ PMove.pm_airaccelerate = 0;
+ }
+
+ SZ.Init(sv.multicast, sv.multicast_buf, sv.multicast_buf.length);
+
+ sv.name = server;
+
+ // leave slots at start for clients only
+ for (i = 0; i < SV_MAIN.maxclients.value; i++) {
+ // needs to reconnect
+ if (svs.clients[i].state > Defines.cs_connected)
+ svs.clients[i].state = Defines.cs_connected;
+ svs.clients[i].lastframe = -1;
+ }
+
+ sv.time = 1000;
+
+ sv.name = server;
+ sv.configstrings[Defines.CS_NAME] = server;
+
+ int iw[] = { checksum };
+
+ if (serverstate != Defines.ss_game) {
+ sv.models[1] = CM.CM_LoadMap("", false, iw); // no real map
+ } else {
+ sv.configstrings[Defines.CS_MODELS + 1] = "maps/" + server + ".bsp";
+ sv.models[1] = CM.CM_LoadMap(
+ sv.configstrings[Defines.CS_MODELS + 1], false, iw);
+ }
+ checksum = iw[0];
+ sv.configstrings[Defines.CS_MAPCHECKSUM] = "" + checksum;
+
+ //
+ // clear physics interaction links
+ //
+ SV_WORLD.SV_ClearWorld();
+
+ for (i = 1; i < CM.CM_NumInlineModels(); i++) {
+ sv.configstrings[Defines.CS_MODELS + 1 + i] = "*" + i;
+ // copy references
+ sv.models[i + 1] = CM
+ .InlineModel(sv.configstrings[Defines.CS_MODELS + 1 + i]);
+ }
+
+ //
+ // spawn the rest of the entities on the map
+ //
+
+ // precache and static commands can be issued during
+ // map initialization
+
+ sv.state = Defines.ss_loading;
+ Globals.server_state = sv.state;
+
+ // load and spawn all other entities
+ GameSpawn.SpawnEntities(sv.name, CM.CM_EntityString(), spawnpoint);
+
+ // run two frames to allow everything to settle
+ GameBase.G_RunFrame();
+ GameBase.G_RunFrame();
+
+ // all precaches are complete
+ sv.state = serverstate;
+ Globals.server_state = sv.state;
+
+ // create a baseline for more efficient communications
+ SV_CreateBaseline();
+
+ // check for a savegame
+ SV_CheckForSavegame();
+
+ // set serverinfo variable
+ Cvar.FullSet("mapname", sv.name, Defines.CVAR_SERVERINFO
+ | Defines.CVAR_NOSET);
+
+ //Com.Printf("-------------------------------------\n");
+ }
+
+ /*
+ * ============== SV_InitGame
+ *
+ * A brand new game has been started ==============
+ */
+ public static void SV_InitGame() {
+ int i;
+ edict_t ent;
+ //char idmaster[32];
+ String idmaster;
+
+ if (svs.initialized) {
+ // cause any connected clients to reconnect
+ SV_MAIN.SV_Shutdown("Server restarted\n", true);
+ } else {
+ // make sure the client is down
+ CL.Drop();
+ SCR.BeginLoadingPlaque();
+ }
+
+ // get any latched variable changes (maxclients, etc)
+ Cvar.GetLatchedVars();
+
+ svs.initialized = true;
+
+ if (Cvar.VariableValue("coop") != 0
+ && Cvar.VariableValue("deathmatch") != 0) {
+ Com.Printf("Deathmatch and Coop both set, disabling Coop\n");
+ Cvar.FullSet("coop", "0", Defines.CVAR_SERVERINFO
+ | Defines.CVAR_LATCH);
+ }
+
+ // dedicated servers are can't be single player and are usually DM
+ // so unless they explicity set coop, force it to deathmatch
+ if (Globals.dedicated.value != 0) {
+ if (0 == Cvar.VariableValue("coop"))
+ Cvar.FullSet("deathmatch", "1", Defines.CVAR_SERVERINFO
+ | Defines.CVAR_LATCH);
+ }
+
+ // init clients
+ if (Cvar.VariableValue("deathmatch") != 0) {
+ if (SV_MAIN.maxclients.value <= 1)
+ Cvar.FullSet("maxclients", "8", Defines.CVAR_SERVERINFO
+ | Defines.CVAR_LATCH);
+ else if (SV_MAIN.maxclients.value > Defines.MAX_CLIENTS)
+ Cvar.FullSet("maxclients", "" + Defines.MAX_CLIENTS,
+ Defines.CVAR_SERVERINFO | Defines.CVAR_LATCH);
+ } else if (Cvar.VariableValue("coop") != 0) {
+ if (SV_MAIN.maxclients.value <= 1 || SV_MAIN.maxclients.value > 4)
+ Cvar.FullSet("maxclients", "4", Defines.CVAR_SERVERINFO
+ | Defines.CVAR_LATCH);
+
+ } else // non-deathmatch, non-coop is one player
+ {
+ Cvar.FullSet("maxclients", "1", Defines.CVAR_SERVERINFO
+ | Defines.CVAR_LATCH);
+ }
+
+ svs.spawncount = Lib.rand();
+ //svs.clients = Z_Malloc(sizeof(client_t) * maxclients.value);
+ svs.clients = new client_t[(int) SV_MAIN.maxclients.value];
+ for (int n = 0; n < svs.clients.length; n++)
+ svs.clients[n] = new client_t();
+
+ svs.num_client_entities = ((int) SV_MAIN.maxclients.value)
+ * Defines.UPDATE_BACKUP * 64; //ok.
+
+ //svs.client_entities = Z_Malloc(sizeof(entity_state_t) *
+ // svs.num_client_entities);
+ svs.client_entities = new entity_state_t[svs.num_client_entities];
+ for (int n = 0; n < svs.client_entities.length; n++)
+ svs.client_entities[n] = new entity_state_t(null);
+
+ // init network stuff
+ NET.Config((SV_MAIN.maxclients.value > 1)); //ok!
+
+ // heartbeats will always be sent to the id master
+ svs.last_heartbeat = -99999; // send immediately
+ idmaster = "192.246.40.37:" + Defines.PORT_MASTER;
+ NET.StringToAdr(idmaster, SV_MAIN.master_adr[0]);
+
+ // init game
+ SV_GAME.SV_InitGameProgs(); // bis hier alles ok!
+
+ for (i = 0; i < SV_MAIN.maxclients.value; i++) {
+ ent = GameBase.g_edicts[i + 1];
+
+ //ent.s.number = i + 1; //dont need this, ent.s.number already set.
+ svs.clients[i].edict = ent;
+ //memset(& svs.clients[i].lastcmd, 0,
+ // sizeof(svs.clients[i].lastcmd));
+ svs.clients[i].lastcmd = new usercmd_t();
+ }
+ }
+
+ /*
+ * ====================== SV_Map
+ *
+ * the full syntax is:
+ *
+ * map [*] <map>$ <startspot>+ <nextserver>
+ *
+ * command from the console or progs. Map can also be a.cin, .pcx, or .dm2
+ * file Nextserver is used to allow a cinematic to play, then proceed to
+ * another level:
+ *
+ * map tram.cin+jail_e3 ======================
+ */
+ public static void SV_Map(boolean attractloop, String levelstring,
+ boolean loadgame) {
+ //char level[MAX_QPATH];
+ //char *ch;
+ int l;
+ //char spawnpoint[MAX_QPATH];
+
+ String level, ch, spawnpoint;
+
+ sv.loadgame = loadgame;
+ sv.attractloop = attractloop;
+
+ if (sv.state == Defines.ss_dead && !sv.loadgame)
+ SV_InitGame(); // the game is just starting
+
+ level = levelstring; // bis hier her ok.
+
+ // if there is a + in the map, set nextserver to the remainder
+
+ //was:
+ // ch = strstr(level, "+");
+ // if (ch)
+ // {
+ // *ch = 0;
+ // Cvar_Set ("nextserver", va("gamemap \"%s\"", ch+1));
+ // }
+ // else
+ // Cvar_Set ("nextserver", "");
+
+ int c = level.indexOf('+');
+ if (c != -1) {
+ Cvar
+ .Set("nextserver", "gamemap \"" + level.substring(c + 1)
+ + "\"");
+ level = level.substring(0, c);
+ } else {
+ Cvar.Set("nextserver", "");
+ }
+
+ //ZOID special hack for end game screen in coop mode
+ if (Cvar.VariableValue("coop") != 0 && !level.equals("victory.pcx"))
+ Cvar.Set("nextserver", "gamemap \"*base1\"");
+
+ // if there is a $, use the remainder as a spawnpoint
+ int pos = level.indexOf('$');
+ if (pos != -1) {
+ //* ch = 0;
+ spawnpoint = level.substring(pos + 1);
+ level = level.substring(0, pos);
+
+ } else
+ //spawnpoint[0] = 0;
+ spawnpoint = "";
+
+ // skip the end-of-unit flag if necessary
+ if (level.charAt(0) == '*')
+ level = level.substring(1);
+
+ l = level.length();
+ if (l > 4 && level.endsWith(".cin")) {
+ SCR.BeginLoadingPlaque(); // for local system
+ SV_SEND.SV_BroadcastCommand("changing\n");
+ SV_SpawnServer(level, spawnpoint, Defines.ss_cinematic,
+ attractloop, loadgame);
+ } else if (l > 4 && level.endsWith(".dm2")) {
+ SCR.BeginLoadingPlaque(); // for local system
+ SV_SEND.SV_BroadcastCommand("changing\n");
+ SV_SpawnServer(level, spawnpoint, Defines.ss_demo, attractloop,
+ loadgame);
+ } else if (l > 4 && level.endsWith(".pcx")) {
+ SCR.BeginLoadingPlaque(); // for local system
+ SV_SEND.SV_BroadcastCommand("changing\n");
+ SV_SpawnServer(level, spawnpoint, Defines.ss_pic, attractloop,
+ loadgame);
+ } else {
+ SCR.BeginLoadingPlaque(); // for local system
+ SV_SEND.SV_BroadcastCommand("changing\n");
+ SV_SEND.SV_SendClientMessages();
+ SV_SpawnServer(level, spawnpoint, Defines.ss_game, attractloop,
+ loadgame);
+ Cbuf.CopyToDefer();
+ }
+
+ SV_SEND.SV_BroadcastCommand("reconnect\n");
+ }
+
+ public static server_static_t svs = new server_static_t(); // persistant
+ // server info
+
+ public static server_t sv = new server_t(); // local server
+} \ No newline at end of file
diff --git a/src/jake2/server/SV_MAIN.java b/src/jake2/server/SV_MAIN.java
index 5800a73..41d5789 100644
--- a/src/jake2/server/SV_MAIN.java
+++ b/src/jake2/server/SV_MAIN.java
@@ -1,1015 +1,1045 @@
/*
-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 13.01.2004 by RST.
-// $Id: SV_MAIN.java,v 1.7 2004-09-10 19:02:56 salomo Exp $
-
+// $Id: SV_MAIN.java,v 1.8 2004-09-22 19:22:12 salomo Exp $
package jake2.server;
import jake2.Defines;
import jake2.Globals;
-import jake2.game.*;
-import jake2.qcommon.*;
+import jake2.game.Cmd;
+import jake2.game.GameBase;
+import jake2.game.Info;
+import jake2.game.PlayerClient;
+import jake2.game.cvar_t;
+import jake2.game.edict_t;
+import jake2.qcommon.Com;
+import jake2.qcommon.Cvar;
+import jake2.qcommon.FS;
+import jake2.qcommon.MSG;
+import jake2.qcommon.Netchan;
+import jake2.qcommon.SZ;
+import jake2.qcommon.netadr_t;
import jake2.sys.NET;
import jake2.sys.Sys;
import jake2.util.Lib;
import java.io.IOException;
-public class SV_MAIN extends SV_GAME {
-
- static netadr_t master_adr[]= new netadr_t[MAX_MASTERS]; // address of group servers
- static {
- for (int i= 0; i < MAX_MASTERS; i++) {
- master_adr[i]= new netadr_t();
- }
- }
- public static client_t sv_client; // current client
-
- public static cvar_t sv_paused;
- public static cvar_t sv_timedemo;
-
- public static cvar_t sv_enforcetime;
-
- public static cvar_t timeout; // seconds without any message
- public static cvar_t zombietime; // seconds to sink messages after disconnect
-
- public static cvar_t rcon_password; // password for remote server commands
-
- public static cvar_t allow_download;
- public static cvar_t allow_download_players;
- public static cvar_t allow_download_models;
- public static cvar_t allow_download_sounds;
- public static cvar_t allow_download_maps;
-
- public static cvar_t sv_airaccelerate;
-
- public static cvar_t sv_noreload; // don't reload level state when reentering
-
- public static cvar_t maxclients; // FIXME: rename sv_maxclients
- public static cvar_t sv_showclamp;
-
- public static cvar_t hostname;
- public static cvar_t public_server; // should heartbeats be sent
-
- public static cvar_t sv_reconnect_limit; // minimum seconds between connect messages
-
- //============================================================================
-
- /*
- =====================
- SV_DropClient
-
- Called when the player is totally leaving the server, either willingly
- or unwillingly. This is NOT called if the entire server is quiting
- or crashing.
- =====================
- */
- public static void SV_DropClient(client_t drop) {
- // add the disconnect
- MSG.WriteByte(drop.netchan.message, Defines.svc_disconnect);
-
- if (drop.state == Defines.cs_spawned) {
- // call the prog function for removing a client
- // this will remove the body, among other things
- PlayerClient.ClientDisconnect(drop.edict);
- }
-
- if (drop.download != null) {
- FS.FreeFile(drop.download);
- drop.download= null;
- }
-
- drop.state= Defines.cs_zombie; // become free in a few seconds
- drop.name= "";
- }
-
- /*
- ==============================================================================
-
- CONNECTIONLESS COMMANDS
-
- ==============================================================================
- */
-
- /*
- ===============
- SV_StatusString
-
- Builds the string that is sent as heartbeats and status replies
- ===============
- */
- public static String SV_StatusString() {
- String player;
- String status= "";
- int i;
- client_t cl;
- int statusLength;
- int playerLength;
-
- status= Cvar.Serverinfo() + "\n";
-
- for (i= 0; i < maxclients.value; i++) {
- cl= svs.clients[i];
- if (cl.state == Defines.cs_connected || cl.state == Defines.cs_spawned) {
- player= "" + cl.edict.client.ps.stats[Defines.STAT_FRAGS] + " " + cl.ping + "\"" + cl.name + "\"\n";
-
- playerLength= player.length();
- statusLength= status.length();
-
- if (statusLength + playerLength >= 1024)
- break; // can't hold any more
-
- status += player;
- }
- }
-
- return status;
- }
-
- /*
- ================
- SVC_Status
-
- Responds with all the info that qplug or qspy can see
- ================
- */
- public static void SVC_Status() {
- Netchan.OutOfBandPrint(NS_SERVER, Netchan.net_from, "print\n" + SV_StatusString());
- }
-
- /*
- ================
- SVC_Ack
-
- ================
- */
- public static void SVC_Ack() {
- Com.Printf("Ping acknowledge from " + NET.AdrToString(Netchan.net_from) + "\n");
- }
-
- /*
- ================
- SVC_Info
-
- Responds with short info for broadcast scans
- The second parameter should be the current protocol version number.
- ================
- */
- public static void SVC_Info() {
- String string;
- int i, count;
- int version;
-
- if (maxclients.value == 1)
- return; // ignore in single player
-
- version= atoi(Cmd.Argv(1));
-
- if (version != PROTOCOL_VERSION)
- string= hostname.string + ": wrong version\n";
- else {
- count= 0;
- for (i= 0; i < maxclients.value; i++)
- if (svs.clients[i].state >= cs_connected)
- count++;
-
- string= hostname.string + " " + sv.name + " " + count + "/" + (int) maxclients.value + "\n";
- }
-
- Netchan.OutOfBandPrint(NS_SERVER, Netchan.net_from, "info\n" + string);
- }
-
- /*
- ================
- SVC_Ping
-
- Just responds with an acknowledgement
- ================
- */
- public static void SVC_Ping() {
- Netchan.OutOfBandPrint(NS_SERVER, Netchan.net_from, "ack");
- }
-
- /*
- =================
- SVC_GetChallenge
-
- Returns a challenge number that can be used
- in a subsequent client_connect command.
- We do this to prevent denial of service attacks that
- flood the server with invalid connection IPs. With a
- challenge, they must give a valid IP address.
- =================
- */
- public static void SVC_GetChallenge() {
- int i;
- int oldest;
- int oldestTime;
-
- oldest= 0;
- oldestTime= 0x7fffffff;
-
- // see if we already have a challenge for this ip
- for (i= 0; i < MAX_CHALLENGES; i++) {
- if (NET.NET_CompareBaseAdr(Netchan.net_from, svs.challenges[i].adr))
- break;
- if (svs.challenges[i].time < oldestTime) {
- oldestTime= svs.challenges[i].time;
- oldest= i;
- }
- }
-
- if (i == MAX_CHALLENGES) {
- // overwrite the oldest
- svs.challenges[oldest].challenge= rand() & 0x7fff;
- svs.challenges[oldest].adr= Netchan.net_from;
- svs.challenges[oldest].time= (int) Globals.curtime;
- i= oldest;
- }
-
- // send it back
- Netchan.OutOfBandPrint(NS_SERVER, Netchan.net_from, "challenge " + svs.challenges[i].challenge);
- }
-
- /*
- ==================
- SVC_DirectConnect
-
- A connection request that did not come from the master
- ==================
- */
- public static void SVC_DirectConnect() {
- String userinfo;
- netadr_t adr;
- int i;
- client_t cl;
-
- edict_t ent;
- int edictnum;
- int version;
- int qport;
-
- adr= Netchan.net_from;
-
- Com.DPrintf("SVC_DirectConnect ()\n");
-
- version= atoi(Cmd.Argv(1));
- if (version != PROTOCOL_VERSION) {
- Netchan.OutOfBandPrint(NS_SERVER, adr, "print\nServer is version " + VERSION + "\n");
- Com.DPrintf(" rejected connect from version " + version + "\n");
- return;
- }
-
- qport= atoi(Cmd.Argv(2));
- int challenge= atoi(Cmd.Argv(3));
- userinfo= Cmd.Argv(4);
-
- //userinfo[sizeof(userinfo) - 1] = 0;
-
- // force the IP key/value pair so the game can filter based on ip
- userinfo= Info.Info_SetValueForKey1(userinfo, "ip", NET.AdrToString(Netchan.net_from));
-
- // attractloop servers are ONLY for local clients
- if (sv.attractloop) {
- if (!NET.IsLocalAddress(adr)) {
- Com.Printf("Remote connect in attract loop. Ignored.\n");
- Netchan.OutOfBandPrint(NS_SERVER, adr, "print\nConnection refused.\n");
- return;
- }
- }
-
- // see if the challenge is valid
- if (!NET.IsLocalAddress(adr)) {
- for (i= 0; i < MAX_CHALLENGES; i++) {
- if (NET.NET_CompareBaseAdr(Netchan.net_from, svs.challenges[i].adr)) {
- if (challenge == svs.challenges[i].challenge)
- break; // good
- Netchan.OutOfBandPrint(NS_SERVER, adr, "print\nBad challenge.\n");
- return;
- }
- }
- if (i == MAX_CHALLENGES) {
- Netchan.OutOfBandPrint(NS_SERVER, adr, "print\nNo challenge for address.\n");
- return;
- }
- }
-
- // if there is already a slot for this ip, reuse it
- for (i= 0; i < maxclients.value; i++) {
- cl= svs.clients[i];
-
- if (cl.state == cs_free)
- continue;
- if (NET.NET_CompareBaseAdr(adr, cl.netchan.remote_address)
- && (cl.netchan.qport == qport || adr.port == cl.netchan.remote_address.port)) {
- if (!NET.IsLocalAddress(adr) && (svs.realtime - cl.lastconnect) < ((int) sv_reconnect_limit.value * 1000)) {
- Com.DPrintf(NET.AdrToString(adr) + ":reconnect rejected : too soon\n");
- return;
- }
- Com.Printf(NET.AdrToString(adr) + ":reconnect\n");
-
- gotnewcl(i, challenge, userinfo, adr, qport);
- return;
- }
- }
-
- // find a client slot
- //newcl = null;
- int index= -1;
- for (i= 0; i < maxclients.value; i++) {
- cl= svs.clients[i];
- if (cl.state == cs_free) {
- index= i;
- break;
- }
- }
- if (index == -1) {
- Netchan.OutOfBandPrint(NS_SERVER, adr, "print\nServer is full.\n");
- Com.DPrintf("Rejected a connection.\n");
- return;
- }
- gotnewcl(index, challenge, userinfo, adr, qport);
- }
-
- public static void gotnewcl(int i, int challenge, String userinfo, netadr_t adr, int qport) {
- // build a new connection
- // accept the new client
- // this is the only place a client_t is ever initialized
- //*newcl = temp;
-
- sv_client= svs.clients[i];
- //edictnum = (newcl-svs.clients)+1;
- int edictnum= i + 1;
- edict_t ent= GameBase.g_edicts[edictnum];
- svs.clients[i].edict= ent;
- svs.clients[i].challenge= challenge; // save challenge for checksumming
-
- // get the game a chance to reject this connection or modify the userinfo
- if (!(PlayerClient.ClientConnect(ent, userinfo))) {
- if (Info.Info_ValueForKey(userinfo, "rejmsg") != null)
- Netchan.OutOfBandPrint(
- NS_SERVER,
- adr,
- "print\n" + Info.Info_ValueForKey(userinfo, "rejmsg") + "\nConnection refused.\n");
- else
- Netchan.OutOfBandPrint(NS_SERVER, adr, "print\nConnection refused.\n");
- Com.DPrintf("Game rejected a connection.\n");
- return;
- }
-
- // parse some info from the info strings
- svs.clients[i].userinfo= userinfo;
- SV_UserinfoChanged(svs.clients[i]);
-
- // send the connect packet to the client
- Netchan.OutOfBandPrint(NS_SERVER, adr, "client_connect");
-
- Netchan.Setup(NS_SERVER, svs.clients[i].netchan, adr, qport);
-
- svs.clients[i].state= cs_connected;
-
- SZ.Init(svs.clients[i].datagram, svs.clients[i].datagram_buf, svs.clients[i].datagram_buf.length);
- svs.clients[i].datagram.allowoverflow= true;
- svs.clients[i].lastmessage= svs.realtime; // don't timeout
- svs.clients[i].lastconnect= svs.realtime;
- Com.DPrintf("new client added.\n");
- }
-
- public static int Rcon_Validate() {
- if (0 == rcon_password.string.length())
- return 0;
-
- if (0 != strcmp(Cmd.Argv(1), rcon_password.string))
- return 0;
-
- return 1;
- }
-
- /*
- ===============
- SVC_RemoteCommand
-
- A client issued an rcon command.
- Shift down the remaining args
- Redirect all printfs
- ===============
- */
- public static void SVC_RemoteCommand() {
- int i;
- //char remaining[1024];
- String remaining;
-
- i= Rcon_Validate();
-
- String msg= new String(net_message.data, 4, -1);
-
- if (i == 0)
- Com.Printf("Bad rcon from " + NET.AdrToString(Netchan.net_from) + ":\n" + msg + "\n");
- else
- Com.Printf("Rcon from " + NET.AdrToString(Netchan.net_from) + ":\n" + msg + "\n");
-
- Com.BeginRedirect(RD_PACKET, SV_SEND.sv_outputbuf, SV_OUTPUTBUF_LENGTH, new Com.RD_Flusher() {
- public void rd_flush(int target, byte[] buffer) {
- SV_SEND.SV_FlushRedirect(target, buffer);
- }
- });
-
- if (0 == Rcon_Validate()) {
- Com.Printf("Bad rcon_password.\n");
- }
- else {
- remaining= "";
-
- for (i= 2; i < Cmd.Argc(); i++) {
- remaining += Cmd.Argv(i);
- remaining += " ";
- }
-
- Cmd.ExecuteString(remaining);
- }
-
- Com.EndRedirect();
- }
-
- /*
- =================
- SV_ConnectionlessPacket
-
- A connectionless packet has four leading 0xff
- characters to distinguish it from a game channel.
- Clients that are in the game can still send
- connectionless packets.
- =================
- */
- public static void SV_ConnectionlessPacket() {
- String s;
- String c;
-
- MSG.BeginReading(net_message);
- MSG.ReadLong(net_message); // skip the -1 marker
-
- s= MSG.ReadStringLine(net_message);
-
- Cmd.TokenizeString(s.toCharArray(), false);
-
- c= Cmd.Argv(0);
- //Com.Printf("Packet " + NET.AdrToString(Netchan.net_from) + " : " + c + "\n");
- //Com.Printf(Lib.hexDump(net_message.data, 64, false) + "\n");
-
- if (0 == strcmp(c, "ping"))
- SVC_Ping();
- else if (0 == strcmp(c, "ack"))
- SVC_Ack();
- else if (0 == strcmp(c, "status"))
- SVC_Status();
- else if (0 == strcmp(c, "info"))
- SVC_Info();
- else if (0 == strcmp(c, "getchallenge"))
- SVC_GetChallenge();
- else if (0 == strcmp(c, "connect"))
- SVC_DirectConnect();
- else if (0 == strcmp(c, "rcon"))
- SVC_RemoteCommand();
- else {
- Com.Printf("bad connectionless packet from " + NET.AdrToString(Netchan.net_from) + "\n");
- Com.Printf("[" + s + "]\n");
- Com.Printf("" + Lib.hexDump(net_message.data, 128, false));
- }
- }
-
- //============================================================================
-
- /*
- ===================
- SV_CalcPings
-
- Updates the cl.ping variables
- ===================
- */
- public static void SV_CalcPings() {
- int i, j;
- client_t cl;
- int total, count;
-
- for (i= 0; i < maxclients.value; i++) {
- cl= svs.clients[i];
- if (cl.state != cs_spawned)
- continue;
-
- total= 0;
- count= 0;
- for (j= 0; j < LATENCY_COUNTS; j++) {
- if (cl.frame_latency[j] > 0) {
- count++;
- total += cl.frame_latency[j];
- }
- }
- if (0 == count)
- cl.ping= 0;
- else
- cl.ping= total / count;
-
- // let the game dll know about the ping
- cl.edict.client.ping= cl.ping;
- }
- }
-
- /*
- ===================
- SV_GiveMsec
-
- Every few frames, gives all clients an allotment of milliseconds
- for their command moves. If they exceed it, assume cheating.
- ===================
- */
- public static void SV_GiveMsec() {
- int i;
- client_t cl;
-
- if ((sv.framenum & 15) != 0)
- return;
-
- for (i= 0; i < maxclients.value; i++) {
- cl= svs.clients[i];
- if (cl.state == cs_free)
- continue;
-
- cl.commandMsec= 1800; // 1600 + some slop
- }
- }
-
- /*
- =================
- SV_ReadPackets
- =================
- */
- public static void SV_ReadPackets() {
- int i;
- client_t cl;
- int qport= 0;
-
- while (NET.GetPacket(NS_SERVER, Netchan.net_from, net_message)) {
-
- // check for connectionless packet (0xffffffff) first
- if ((net_message.data[0] == -1)
- && (net_message.data[1] == -1)
- && (net_message.data[2] == -1)
- && (net_message.data[3] == -1)) {
- SV_ConnectionlessPacket();
- continue;
- }
-
- // read the qport out of the message so we can fix up
- // stupid address translating routers
- MSG.BeginReading(net_message);
- MSG.ReadLong(net_message); // sequence number
- MSG.ReadLong(net_message); // sequence number
- qport= MSG.ReadShort(net_message) & 0xffff;
-
- // check for packets from connected clients
- for (i= 0; i < maxclients.value; i++) {
- cl= svs.clients[i];
- if (cl.state == cs_free)
- continue;
- if (!NET.NET_CompareBaseAdr(Netchan.net_from, cl.netchan.remote_address))
- continue;
- if (cl.netchan.qport != qport)
- continue;
- if (cl.netchan.remote_address.port != Netchan.net_from.port) {
- Com.Printf("SV_ReadPackets: fixing up a translated port\n");
- cl.netchan.remote_address.port= Netchan.net_from.port;
- }
-
- if (Netchan.Process(cl.netchan, net_message)) { // this is a valid, sequenced packet, so process it
- if (cl.state != cs_zombie) {
- cl.lastmessage= svs.realtime; // don't timeout
- SV_USER.SV_ExecuteClientMessage(cl);
- }
- }
- break;
- }
-
- if (i != maxclients.value)
- continue;
- }
- }
-
- /*
- ==================
- SV_CheckTimeouts
-
- If a packet has not been received from a client for timeout.value
- seconds, drop the conneciton. Server frames are used instead of
- realtime to avoid dropping the local client while debugging.
-
- When a client is normally dropped, the client_t goes into a zombie state
- for a few seconds to make sure any final reliable message gets resent
- if necessary
- ==================
- */
- public static void SV_CheckTimeouts() {
- int i;
- client_t cl;
- int droppoint;
- int zombiepoint;
-
- droppoint= (int) (svs.realtime - 1000 * timeout.value);
- zombiepoint= (int) (svs.realtime - 1000 * zombietime.value);
-
- for (i= 0; i < maxclients.value; i++) {
- cl= svs.clients[i];
- // message times may be wrong across a changelevel
- if (cl.lastmessage > svs.realtime)
- cl.lastmessage= svs.realtime;
-
- if (cl.state == cs_zombie && cl.lastmessage < zombiepoint) {
- cl.state= cs_free; // can now be reused
- continue;
- }
- if ((cl.state == cs_connected || cl.state == cs_spawned) && cl.lastmessage < droppoint) {
- SV_SEND.SV_BroadcastPrintf(PRINT_HIGH, cl.name + " timed out\n");
- SV_DropClient(cl);
- cl.state= cs_free; // don't bother with zombie state
- }
- }
- }
-
- /*
- ================
- SV_PrepWorldFrame
-
- This has to be done before the world logic, because
- player processing happens outside RunWorldFrame
- ================
- */
- public static void SV_PrepWorldFrame() {
- edict_t ent;
- int i;
-
- for (i= 0; i < GameBase.num_edicts; i++) {
- ent= GameBase.g_edicts[i];
- // events only last for a single message
- ent.s.event= 0;
- }
-
- }
-
- /*
- =================
- SV_RunGameFrame
- =================
- */
- public static void SV_RunGameFrame() {
- if (host_speeds.value != 0)
- time_before_game= Sys.Milliseconds();
-
- // we always need to bump framenum, even if we
- // don't run the world, otherwise the delta
- // compression can get confused when a client
- // has the "current" frame
- sv.framenum++;
- sv.time= sv.framenum * 100;
-
- // don't run if paused
- if (0 == sv_paused.value || maxclients.value > 1) {
- Game.G_RunFrame();
-
- // never get more than one tic behind
- if (sv.time < svs.realtime) {
- if (sv_showclamp.value != 0)
- Com.Printf("sv highclamp\n");
- svs.realtime= sv.time;
- }
- }
-
- if (host_speeds.value != 0)
- time_after_game= Sys.Milliseconds();
-
- }
-
- /*
- ==================
- SV_Frame
-
- ==================
- */
- public static void SV_Frame(long msec) {
- Globals.time_before_game= Globals.time_after_game= 0;
-
- // if server is not active, do nothing
- if (!svs.initialized)
- return;
-
- svs.realtime += msec;
-
- // keep the random time dependent
- Lib.rand();
-
- // check timeouts
- SV_CheckTimeouts();
-
- // get packets from clients
- SV_ReadPackets();
-
- //if (Game.g_edicts[1] !=null)
- // Com.p("player at:" + Lib.vtofsbeaty(Game.g_edicts[1].s.origin ));
-
- // move autonomous things around if enough time has passed
- if (0 == sv_timedemo.value && svs.realtime < sv.time) {
- // never let the time get too far off
- if (sv.time - svs.realtime > 100) {
- if (sv_showclamp.value != 0)
- Com.Printf("sv lowclamp\n");
- svs.realtime= sv.time - 100;
- }
- NET.NET_Sleep(sv.time - svs.realtime);
- return;
- }
-
- // update ping based on the last known frame from all clients
- //TODO: dont need yet
- SV_CalcPings();
-
- // give the clients some timeslices
- //TODO: dont need yet
- SV_GiveMsec();
-
- // let everything in the world think and move
- SV_RunGameFrame();
-
- // send messages back to the clients that had packets read this frame
- SV_SEND.SV_SendClientMessages();
-
- // save the entire world state if recording a serverdemo
- //TODO: dont need yet
- //SV_WORLD.SV_RecordDemoMessage();
-
- // send a heartbeat to the master if needed
- //TODO: dont need yet
- Master_Heartbeat();
-
- // clear teleport flags, etc for next frame
- SV_PrepWorldFrame();
-
- }
-
- //============================================================================
-
- /*
- ================
- Master_Heartbeat
-
- Send a message to the master every few minutes to
- let it know we are alive, and log information
- ================
- */
- public static final int HEARTBEAT_SECONDS= 300;
- public static void Master_Heartbeat() {
- String string;
- int i;
-
- // pgm post3.19 change, cvar pointer not validated before dereferencing
- if (dedicated == null || 0 == dedicated.value)
- return; // only dedicated servers send heartbeats
-
- // pgm post3.19 change, cvar pointer not validated before dereferencing
- if (null == public_server || 0 == public_server.value)
- return; // a private dedicated game
-
- // check for time wraparound
- if (svs.last_heartbeat > svs.realtime)
- svs.last_heartbeat= svs.realtime;
-
- if (svs.realtime - svs.last_heartbeat < HEARTBEAT_SECONDS * 1000)
- return; // not time to send yet
-
- svs.last_heartbeat= svs.realtime;
-
- // send the same string that we would give for a status OOB command
- string= SV_StatusString();
-
- // send to group master
- for (i= 0; i < MAX_MASTERS; i++)
- if (master_adr[i].port != 0) {
- Com.Printf("Sending heartbeat to " + NET.AdrToString(master_adr[i]) + "\n");
- Netchan.OutOfBandPrint(NS_SERVER, master_adr[i], "heartbeat\n" + string);
- }
- }
-
- /*
- =================
- Master_Shutdown
-
- Informs all masters that this server is going down
- =================
- */
- static void Master_Shutdown() {
- int i;
-
- // pgm post3.19 change, cvar pointer not validated before dereferencing
- if (null == dedicated || 0 == dedicated.value)
- return; // only dedicated servers send heartbeats
-
- // pgm post3.19 change, cvar pointer not validated before dereferencing
- if (null == public_server || 0 == public_server.value)
- return; // a private dedicated game
-
- // send to group master
- for (i= 0; i < MAX_MASTERS; i++)
- if (master_adr[i].port != 0) {
- if (i > 0)
- Com.Printf("Sending heartbeat to " + NET.AdrToString(master_adr[i]) + "\n");
- Netchan.OutOfBandPrint(NS_SERVER, master_adr[i], "shutdown");
- }
- }
-
- //============================================================================
-
- /*
- =================
- SV_UserinfoChanged
-
- Pull specific info from a newly changed userinfo string
- into a more C freindly form.
- =================
- */
- public static void SV_UserinfoChanged(client_t cl) {
- String val;
- int i;
-
- // call prog code to allow overrides
- PlayerClient.ClientUserinfoChanged(cl.edict, cl.userinfo);
-
- // name for C code
- cl.name= Info.Info_ValueForKey(cl.userinfo, "name");
-
- // mask off high bit
- //TODO: masking for german umlaute
- //for (i=0 ; i<sizeof(cl.name) ; i++)
- // cl.name[i] &= 127;
-
- // rate command
- val= Info.Info_ValueForKey(cl.userinfo, "rate");
- if (val.length() > 0) {
- i= atoi(val);
- cl.rate= i;
- if (cl.rate < 100)
- cl.rate= 100;
- if (cl.rate > 15000)
- cl.rate= 15000;
- }
- else
- cl.rate= 5000;
-
- // msg command
- val= Info.Info_ValueForKey(cl.userinfo, "msg");
- if (val.length() > 0) {
- cl.messagelevel= atoi(val);
- }
-
- }
-
- //============================================================================
-
- /*
- ===============
- SV_Init
-
- Only called at quake2.exe startup, not for each game
- ===============
- */
- public static void SV_Init() {
- SV_CCMDS.SV_InitOperatorCommands(); //ok.
-
- rcon_password= Cvar.Get("rcon_password", "", 0);
- Cvar.Get("skill", "1", 0);
- Cvar.Get("deathmatch", "0", CVAR_LATCH);
- Cvar.Get("coop", "0", CVAR_LATCH);
- Cvar.Get("dmflags", "" + DF_INSTANT_ITEMS, CVAR_SERVERINFO);
- Cvar.Get("fraglimit", "0", CVAR_SERVERINFO);
- Cvar.Get("timelimit", "0", CVAR_SERVERINFO);
- //TODO: set cheats 0
- Cvar.Get("cheats", "1", CVAR_SERVERINFO | CVAR_LATCH);
- Cvar.Get("protocol", "" + PROTOCOL_VERSION, CVAR_SERVERINFO | CVAR_NOSET);
-
- SV_MAIN.maxclients= Cvar.Get("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH);
- hostname= Cvar.Get("hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE);
- timeout= Cvar.Get("timeout", "125", 0);
- zombietime= Cvar.Get("zombietime", "2", 0);
- sv_showclamp= Cvar.Get("showclamp", "0", 0);
- sv_paused= Cvar.Get("paused", "0", 0);
- sv_timedemo= Cvar.Get("timedemo", "0", 0);
- sv_enforcetime= Cvar.Get("sv_enforcetime", "0", 0);
-
- // TODO: carsten, re-allow downloads per default
- allow_download= Cvar.Get("allow_download", "0", CVAR_ARCHIVE);
- allow_download_players= Cvar.Get("allow_download_players", "0", CVAR_ARCHIVE);
- allow_download_models= Cvar.Get("allow_download_models", "1", CVAR_ARCHIVE);
- allow_download_sounds= Cvar.Get("allow_download_sounds", "1", CVAR_ARCHIVE);
- allow_download_maps= Cvar.Get("allow_download_maps", "1", CVAR_ARCHIVE);
-
- sv_noreload= Cvar.Get("sv_noreload", "0", 0);
- sv_airaccelerate= Cvar.Get("sv_airaccelerate", "0", CVAR_LATCH);
- public_server= Cvar.Get("public", "0", 0);
- sv_reconnect_limit= Cvar.Get("sv_reconnect_limit", "3", CVAR_ARCHIVE);
-
- SZ.Init(net_message, net_message_buffer, net_message_buffer.length);
- }
-
- /*
- ==================
- SV_FinalMessage
-
- Used by SV_Shutdown to send a final message to all
- connected clients before the server goes down. The messages are sent immediately,
- not just stuck on the outgoing message list, because the server is going
- to totally exit after returning from this function.
- ==================
- */
- public static void SV_FinalMessage(String message, boolean reconnect) {
- int i;
- client_t cl;
-
- SZ.Clear(net_message);
- MSG.WriteByte(net_message, svc_print);
- MSG.WriteByte(net_message, PRINT_HIGH);
- MSG.WriteString(net_message, message);
-
- if (reconnect)
- MSG.WriteByte(net_message, svc_reconnect);
- else
- MSG.WriteByte(net_message, svc_disconnect);
-
- // send it twice
- // stagger the packets to crutch operating system limited buffers
-
- for (i= 0; i < maxclients.value; i++) {
- cl= svs.clients[i];
- if (cl.state >= cs_connected)
- Netchan.Transmit(cl.netchan, net_message.cursize, net_message.data);
- }
- for (i= 0; i < maxclients.value; i++) {
- cl= svs.clients[i];
- if (cl.state >= cs_connected)
- Netchan.Transmit(cl.netchan, net_message.cursize, net_message.data);
- }
- }
-
- /*
- ================
- SV_Shutdown
-
- Called when each game quits,
- before Sys_Quit or Sys_Error
- ================
- */
- public static void SV_Shutdown(String finalmsg, boolean reconnect) {
- if (svs.clients != null)
- SV_FinalMessage(finalmsg, reconnect);
-
- Master_Shutdown();
-
- SV_GAME.SV_ShutdownGameProgs();
-
- // free current level
- if (sv.demofile != null)
- try {
- sv.demofile.close();
- }
- catch (IOException e) {
- e.printStackTrace();
- }
-
- sv= new server_t();
-
- Globals.server_state= sv.state;
-
- if (svs.demofile != null)
- try {
- svs.demofile.close();
- }
- catch (IOException e1) {
- e1.printStackTrace();
- }
-
- svs= new server_static_t();
- }
-}
+public class SV_MAIN {
+
+ public static netadr_t master_adr[] = new netadr_t[Defines.MAX_MASTERS]; // address
+ // of
+ // group
+ // servers
+ static {
+ for (int i = 0; i < Defines.MAX_MASTERS; i++) {
+ master_adr[i] = new netadr_t();
+ }
+ }
+
+ public static client_t sv_client; // current client
+
+ public static cvar_t sv_paused;
+
+ public static cvar_t sv_timedemo;
+
+ public static cvar_t sv_enforcetime;
+
+ public static cvar_t timeout; // seconds without any message
+
+ public static cvar_t zombietime; // seconds to sink messages after
+ // disconnect
+
+ public static cvar_t rcon_password; // password for remote server commands
+
+ public static cvar_t allow_download;
+
+ public static cvar_t allow_download_players;
+
+ public static cvar_t allow_download_models;
+
+ public static cvar_t allow_download_sounds;
+
+ public static cvar_t allow_download_maps;
+
+ public static cvar_t sv_airaccelerate;
+
+ public static cvar_t sv_noreload; // don't reload level state when
+ // reentering
+
+ public static cvar_t maxclients; // FIXME: rename sv_maxclients
+
+ public static cvar_t sv_showclamp;
+
+ public static cvar_t hostname;
+
+ public static cvar_t public_server; // should heartbeats be sent
+
+ public static cvar_t sv_reconnect_limit; // minimum seconds between connect
+ // messages
+
+ //============================================================================
+
+ /*
+ * ================ Master_Heartbeat
+ *
+ * Send a message to the master every few minutes to let it know we are
+ * alive, and log information ================
+ */
+ public static final int HEARTBEAT_SECONDS = 300;
+
+ //============================================================================
+
+ /*
+ * ===================== SV_DropClient
+ *
+ * Called when the player is totally leaving the server, either willingly or
+ * unwillingly. This is NOT called if the entire server is quiting or
+ * crashing. =====================
+ */
+ public static void SV_DropClient(client_t drop) {
+ // add the disconnect
+ MSG.WriteByte(drop.netchan.message, Defines.svc_disconnect);
+
+ if (drop.state == Defines.cs_spawned) {
+ // call the prog function for removing a client
+ // this will remove the body, among other things
+ PlayerClient.ClientDisconnect(drop.edict);
+ }
+
+ if (drop.download != null) {
+ FS.FreeFile(drop.download);
+ drop.download = null;
+ }
+
+ drop.state = Defines.cs_zombie; // become free in a few seconds
+ drop.name = "";
+ }
+
+ /*
+ * ==============================================================================
+ *
+ * CONNECTIONLESS COMMANDS
+ *
+ * ==============================================================================
+ */
+
+ /*
+ * =============== SV_StatusString
+ *
+ * Builds the string that is sent as heartbeats and status replies
+ * ===============
+ */
+ public static String SV_StatusString() {
+ String player;
+ String status = "";
+ int i;
+ client_t cl;
+ int statusLength;
+ int playerLength;
+
+ status = Cvar.Serverinfo() + "\n";
+
+ for (i = 0; i < SV_MAIN.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (cl.state == Defines.cs_connected
+ || cl.state == Defines.cs_spawned) {
+ player = "" + cl.edict.client.ps.stats[Defines.STAT_FRAGS]
+ + " " + cl.ping + "\"" + cl.name + "\"\n";
+
+ playerLength = player.length();
+ statusLength = status.length();
+
+ if (statusLength + playerLength >= 1024)
+ break; // can't hold any more
+
+ status += player;
+ }
+ }
+
+ return status;
+ }
+
+ /*
+ * ================ SVC_Status
+ *
+ * Responds with all the info that qplug or qspy can see ================
+ */
+ public static void SVC_Status() {
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, Globals.net_from, "print\n"
+ + SV_StatusString());
+ }
+
+ /*
+ * ================ SVC_Ack
+ *
+ * ================
+ */
+ public static void SVC_Ack() {
+ Com.Printf("Ping acknowledge from " + NET.AdrToString(Globals.net_from)
+ + "\n");
+ }
+
+ /*
+ * ================ SVC_Info
+ *
+ * Responds with short info for broadcast scans The second parameter should
+ * be the current protocol version number. ================
+ */
+ public static void SVC_Info() {
+ String string;
+ int i, count;
+ int version;
+
+ if (SV_MAIN.maxclients.value == 1)
+ return; // ignore in single player
+
+ version = Lib.atoi(Cmd.Argv(1));
+
+ if (version != Defines.PROTOCOL_VERSION)
+ string = SV_MAIN.hostname.string + ": wrong version\n";
+ else {
+ count = 0;
+ for (i = 0; i < SV_MAIN.maxclients.value; i++)
+ if (SV_INIT.svs.clients[i].state >= Defines.cs_connected)
+ count++;
+
+ string = SV_MAIN.hostname.string + " " + SV_INIT.sv.name + " "
+ + count + "/" + (int) SV_MAIN.maxclients.value + "\n";
+ }
+
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, Globals.net_from, "info\n"
+ + string);
+ }
+
+ /*
+ * ================ SVC_Ping
+ *
+ * Just responds with an acknowledgement ================
+ */
+ public static void SVC_Ping() {
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, Globals.net_from, "ack");
+ }
+
+ /*
+ * ================= SVC_GetChallenge
+ *
+ * Returns a challenge number that can be used in a subsequent
+ * client_connect command. We do this to prevent denial of service attacks
+ * that flood the server with invalid connection IPs. With a challenge, they
+ * must give a valid IP address. =================
+ */
+ public static void SVC_GetChallenge() {
+ int i;
+ int oldest;
+ int oldestTime;
+
+ oldest = 0;
+ oldestTime = 0x7fffffff;
+
+ // see if we already have a challenge for this ip
+ for (i = 0; i < Defines.MAX_CHALLENGES; i++) {
+ if (NET.NET_CompareBaseAdr(Globals.net_from,
+ SV_INIT.svs.challenges[i].adr))
+ break;
+ if (SV_INIT.svs.challenges[i].time < oldestTime) {
+ oldestTime = SV_INIT.svs.challenges[i].time;
+ oldest = i;
+ }
+ }
+
+ if (i == Defines.MAX_CHALLENGES) {
+ // overwrite the oldest
+ SV_INIT.svs.challenges[oldest].challenge = Lib.rand() & 0x7fff;
+ SV_INIT.svs.challenges[oldest].adr = Globals.net_from;
+ SV_INIT.svs.challenges[oldest].time = (int) Globals.curtime;
+ i = oldest;
+ }
+
+ // send it back
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, Globals.net_from,
+ "challenge " + SV_INIT.svs.challenges[i].challenge);
+ }
+
+ /*
+ * ================== SVC_DirectConnect
+ *
+ * A connection request that did not come from the master ==================
+ */
+ public static void SVC_DirectConnect() {
+ String userinfo;
+ netadr_t adr;
+ int i;
+ client_t cl;
+
+ edict_t ent;
+ int edictnum;
+ int version;
+ int qport;
+
+ adr = Globals.net_from;
+
+ Com.DPrintf("SVC_DirectConnect ()\n");
+
+ version = Lib.atoi(Cmd.Argv(1));
+ if (version != Defines.PROTOCOL_VERSION) {
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, adr,
+ "print\nServer is version " + Globals.VERSION + "\n");
+ Com.DPrintf(" rejected connect from version " + version + "\n");
+ return;
+ }
+
+ qport = Lib.atoi(Cmd.Argv(2));
+ int challenge = Lib.atoi(Cmd.Argv(3));
+ userinfo = Cmd.Argv(4);
+
+ //userinfo[sizeof(userinfo) - 1] = 0;
+
+ // force the IP key/value pair so the game can filter based on ip
+ userinfo = Info.Info_SetValueForKey1(userinfo, "ip", NET
+ .AdrToString(Globals.net_from));
+
+ // attractloop servers are ONLY for local clients
+ if (SV_INIT.sv.attractloop) {
+ if (!NET.IsLocalAddress(adr)) {
+ Com.Printf("Remote connect in attract loop. Ignored.\n");
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, adr,
+ "print\nConnection refused.\n");
+ return;
+ }
+ }
+
+ // see if the challenge is valid
+ if (!NET.IsLocalAddress(adr)) {
+ for (i = 0; i < Defines.MAX_CHALLENGES; i++) {
+ if (NET.NET_CompareBaseAdr(Globals.net_from,
+ SV_INIT.svs.challenges[i].adr)) {
+ if (challenge == SV_INIT.svs.challenges[i].challenge)
+ break; // good
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, adr,
+ "print\nBad challenge.\n");
+ return;
+ }
+ }
+ if (i == Defines.MAX_CHALLENGES) {
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, adr,
+ "print\nNo challenge for address.\n");
+ return;
+ }
+ }
+
+ // if there is already a slot for this ip, reuse it
+ for (i = 0; i < SV_MAIN.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+
+ if (cl.state == Defines.cs_free)
+ continue;
+ if (NET.NET_CompareBaseAdr(adr, cl.netchan.remote_address)
+ && (cl.netchan.qport == qport || adr.port == cl.netchan.remote_address.port)) {
+ if (!NET.IsLocalAddress(adr)
+ && (SV_INIT.svs.realtime - cl.lastconnect) < ((int) SV_MAIN.sv_reconnect_limit.value * 1000)) {
+ Com.DPrintf(NET.AdrToString(adr)
+ + ":reconnect rejected : too soon\n");
+ return;
+ }
+ Com.Printf(NET.AdrToString(adr) + ":reconnect\n");
+
+ gotnewcl(i, challenge, userinfo, adr, qport);
+ return;
+ }
+ }
+
+ // find a client slot
+ //newcl = null;
+ int index = -1;
+ for (i = 0; i < SV_MAIN.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (cl.state == Defines.cs_free) {
+ index = i;
+ break;
+ }
+ }
+ if (index == -1) {
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, adr,
+ "print\nServer is full.\n");
+ Com.DPrintf("Rejected a connection.\n");
+ return;
+ }
+ gotnewcl(index, challenge, userinfo, adr, qport);
+ }
+
+ public static void gotnewcl(int i, int challenge, String userinfo,
+ netadr_t adr, int qport) {
+ // build a new connection
+ // accept the new client
+ // this is the only place a client_t is ever initialized
+ //*newcl = temp;
+
+ SV_MAIN.sv_client = SV_INIT.svs.clients[i];
+ //edictnum = (newcl-svs.clients)+1;
+ int edictnum = i + 1;
+ edict_t ent = GameBase.g_edicts[edictnum];
+ SV_INIT.svs.clients[i].edict = ent;
+ SV_INIT.svs.clients[i].challenge = challenge; // save challenge for
+ // checksumming
+
+ // get the game a chance to reject this connection or modify the
+ // userinfo
+ if (!(PlayerClient.ClientConnect(ent, userinfo))) {
+ if (Info.Info_ValueForKey(userinfo, "rejmsg") != null)
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, adr, "print\n"
+ + Info.Info_ValueForKey(userinfo, "rejmsg")
+ + "\nConnection refused.\n");
+ else
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, adr,
+ "print\nConnection refused.\n");
+ Com.DPrintf("Game rejected a connection.\n");
+ return;
+ }
+
+ // parse some info from the info strings
+ SV_INIT.svs.clients[i].userinfo = userinfo;
+ SV_UserinfoChanged(SV_INIT.svs.clients[i]);
+
+ // send the connect packet to the client
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, adr, "client_connect");
+
+ Netchan.Setup(Defines.NS_SERVER, SV_INIT.svs.clients[i].netchan, adr,
+ qport);
+
+ SV_INIT.svs.clients[i].state = Defines.cs_connected;
+
+ SZ.Init(SV_INIT.svs.clients[i].datagram,
+ SV_INIT.svs.clients[i].datagram_buf,
+ SV_INIT.svs.clients[i].datagram_buf.length);
+ SV_INIT.svs.clients[i].datagram.allowoverflow = true;
+ SV_INIT.svs.clients[i].lastmessage = SV_INIT.svs.realtime; // don't
+ // timeout
+ SV_INIT.svs.clients[i].lastconnect = SV_INIT.svs.realtime;
+ Com.DPrintf("new client added.\n");
+ }
+
+ public static int Rcon_Validate() {
+ if (0 == SV_MAIN.rcon_password.string.length())
+ return 0;
+
+ if (0 != Lib.strcmp(Cmd.Argv(1), SV_MAIN.rcon_password.string))
+ return 0;
+
+ return 1;
+ }
+
+ /*
+ * =============== SVC_RemoteCommand
+ *
+ * A client issued an rcon command. Shift down the remaining args Redirect
+ * all printfs ===============
+ */
+ public static void SVC_RemoteCommand() {
+ int i;
+ //char remaining[1024];
+ String remaining;
+
+ i = Rcon_Validate();
+
+ String msg = new String(Globals.net_message.data, 4, -1);
+
+ if (i == 0)
+ Com.Printf("Bad rcon from " + NET.AdrToString(Globals.net_from)
+ + ":\n" + msg + "\n");
+ else
+ Com.Printf("Rcon from " + NET.AdrToString(Globals.net_from) + ":\n"
+ + msg + "\n");
+
+ Com.BeginRedirect(Defines.RD_PACKET, SV_SEND.sv_outputbuf,
+ Defines.SV_OUTPUTBUF_LENGTH, new Com.RD_Flusher() {
+ public void rd_flush(int target, byte[] buffer) {
+ SV_SEND.SV_FlushRedirect(target, buffer);
+ }
+ });
+
+ if (0 == Rcon_Validate()) {
+ Com.Printf("Bad rcon_password.\n");
+ } else {
+ remaining = "";
+
+ for (i = 2; i < Cmd.Argc(); i++) {
+ remaining += Cmd.Argv(i);
+ remaining += " ";
+ }
+
+ Cmd.ExecuteString(remaining);
+ }
+
+ Com.EndRedirect();
+ }
+
+ /*
+ * ================= SV_ConnectionlessPacket
+ *
+ * A connectionless packet has four leading 0xff characters to distinguish
+ * it from a game channel. Clients that are in the game can still send
+ * connectionless packets. =================
+ */
+ public static void SV_ConnectionlessPacket() {
+ String s;
+ String c;
+
+ MSG.BeginReading(Globals.net_message);
+ MSG.ReadLong(Globals.net_message); // skip the -1 marker
+
+ s = MSG.ReadStringLine(Globals.net_message);
+
+ Cmd.TokenizeString(s.toCharArray(), false);
+
+ c = Cmd.Argv(0);
+ //Com.Printf("Packet " + NET.AdrToString(Netchan.net_from) + " : " + c
+ // + "\n");
+ //Com.Printf(Lib.hexDump(net_message.data, 64, false) + "\n");
+
+ if (0 == Lib.strcmp(c, "ping"))
+ SVC_Ping();
+ else if (0 == Lib.strcmp(c, "ack"))
+ SVC_Ack();
+ else if (0 == Lib.strcmp(c, "status"))
+ SVC_Status();
+ else if (0 == Lib.strcmp(c, "info"))
+ SVC_Info();
+ else if (0 == Lib.strcmp(c, "getchallenge"))
+ SVC_GetChallenge();
+ else if (0 == Lib.strcmp(c, "connect"))
+ SVC_DirectConnect();
+ else if (0 == Lib.strcmp(c, "rcon"))
+ SVC_RemoteCommand();
+ else {
+ Com.Printf("bad connectionless packet from "
+ + NET.AdrToString(Globals.net_from) + "\n");
+ Com.Printf("[" + s + "]\n");
+ Com.Printf("" + Lib.hexDump(Globals.net_message.data, 128, false));
+ }
+ }
+
+ //============================================================================
+
+ /*
+ * =================== SV_CalcPings
+ *
+ * Updates the cl.ping variables ===================
+ */
+ public static void SV_CalcPings() {
+ int i, j;
+ client_t cl;
+ int total, count;
+
+ for (i = 0; i < SV_MAIN.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (cl.state != Defines.cs_spawned)
+ continue;
+
+ total = 0;
+ count = 0;
+ for (j = 0; j < Defines.LATENCY_COUNTS; j++) {
+ if (cl.frame_latency[j] > 0) {
+ count++;
+ total += cl.frame_latency[j];
+ }
+ }
+ if (0 == count)
+ cl.ping = 0;
+ else
+ cl.ping = total / count;
+
+ // let the game dll know about the ping
+ cl.edict.client.ping = cl.ping;
+ }
+ }
+
+ /*
+ * =================== SV_GiveMsec
+ *
+ * Every few frames, gives all clients an allotment of milliseconds for
+ * their command moves. If they exceed it, assume cheating.
+ * ===================
+ */
+ public static void SV_GiveMsec() {
+ int i;
+ client_t cl;
+
+ if ((SV_INIT.sv.framenum & 15) != 0)
+ return;
+
+ for (i = 0; i < SV_MAIN.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (cl.state == Defines.cs_free)
+ continue;
+
+ cl.commandMsec = 1800; // 1600 + some slop
+ }
+ }
+
+ /*
+ * ================= SV_ReadPackets =================
+ */
+ public static void SV_ReadPackets() {
+ int i;
+ client_t cl;
+ int qport = 0;
+
+ while (NET.GetPacket(Defines.NS_SERVER, Globals.net_from,
+ Globals.net_message)) {
+
+ // check for connectionless packet (0xffffffff) first
+ if ((Globals.net_message.data[0] == -1)
+ && (Globals.net_message.data[1] == -1)
+ && (Globals.net_message.data[2] == -1)
+ && (Globals.net_message.data[3] == -1)) {
+ SV_ConnectionlessPacket();
+ continue;
+ }
+
+ // read the qport out of the message so we can fix up
+ // stupid address translating routers
+ MSG.BeginReading(Globals.net_message);
+ MSG.ReadLong(Globals.net_message); // sequence number
+ MSG.ReadLong(Globals.net_message); // sequence number
+ qport = MSG.ReadShort(Globals.net_message) & 0xffff;
+
+ // check for packets from connected clients
+ for (i = 0; i < SV_MAIN.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (cl.state == Defines.cs_free)
+ continue;
+ if (!NET.NET_CompareBaseAdr(Globals.net_from,
+ cl.netchan.remote_address))
+ continue;
+ if (cl.netchan.qport != qport)
+ continue;
+ if (cl.netchan.remote_address.port != Globals.net_from.port) {
+ Com.Printf("SV_ReadPackets: fixing up a translated port\n");
+ cl.netchan.remote_address.port = Globals.net_from.port;
+ }
+
+ if (Netchan.Process(cl.netchan, Globals.net_message)) {
+ // this is a valid, sequenced packet, so process it
+ if (cl.state != Defines.cs_zombie) {
+ cl.lastmessage = SV_INIT.svs.realtime; // don't timeout
+ SV_USER.SV_ExecuteClientMessage(cl);
+ }
+ }
+ break;
+ }
+
+ if (i != SV_MAIN.maxclients.value)
+ continue;
+ }
+ }
+
+ /*
+ * ================== SV_CheckTimeouts
+ *
+ * If a packet has not been received from a client for timeout.value
+ * seconds, drop the conneciton. Server frames are used instead of realtime
+ * to avoid dropping the local client while debugging.
+ *
+ * When a client is normally dropped, the client_t goes into a zombie state
+ * for a few seconds to make sure any final reliable message gets resent if
+ * necessary ==================
+ */
+ public static void SV_CheckTimeouts() {
+ int i;
+ client_t cl;
+ int droppoint;
+ int zombiepoint;
+
+ droppoint = (int) (SV_INIT.svs.realtime - 1000 * SV_MAIN.timeout.value);
+ zombiepoint = (int) (SV_INIT.svs.realtime - 1000 * SV_MAIN.zombietime.value);
+
+ for (i = 0; i < SV_MAIN.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ // message times may be wrong across a changelevel
+ if (cl.lastmessage > SV_INIT.svs.realtime)
+ cl.lastmessage = SV_INIT.svs.realtime;
+
+ if (cl.state == Defines.cs_zombie && cl.lastmessage < zombiepoint) {
+ cl.state = Defines.cs_free; // can now be reused
+ continue;
+ }
+ if ((cl.state == Defines.cs_connected || cl.state == Defines.cs_spawned)
+ && cl.lastmessage < droppoint) {
+ SV_SEND.SV_BroadcastPrintf(Defines.PRINT_HIGH, cl.name
+ + " timed out\n");
+ SV_DropClient(cl);
+ cl.state = Defines.cs_free; // don't bother with zombie state
+ }
+ }
+ }
+
+ /*
+ * ================ SV_PrepWorldFrame
+ *
+ * This has to be done before the world logic, because player processing
+ * happens outside RunWorldFrame ================
+ */
+ public static void SV_PrepWorldFrame() {
+ edict_t ent;
+ int i;
+
+ for (i = 0; i < GameBase.num_edicts; i++) {
+ ent = GameBase.g_edicts[i];
+ // events only last for a single message
+ ent.s.event = 0;
+ }
+
+ }
+
+ /*
+ * ================= SV_RunGameFrame =================
+ */
+ public static void SV_RunGameFrame() {
+ if (Globals.host_speeds.value != 0)
+ Globals.time_before_game = Sys.Milliseconds();
+
+ // we always need to bump framenum, even if we
+ // don't run the world, otherwise the delta
+ // compression can get confused when a client
+ // has the "current" frame
+ SV_INIT.sv.framenum++;
+ SV_INIT.sv.time = SV_INIT.sv.framenum * 100;
+
+ // don't run if paused
+ if (0 == SV_MAIN.sv_paused.value || SV_MAIN.maxclients.value > 1) {
+ GameBase.G_RunFrame();
+
+ // never get more than one tic behind
+ if (SV_INIT.sv.time < SV_INIT.svs.realtime) {
+ if (SV_MAIN.sv_showclamp.value != 0)
+ Com.Printf("sv highclamp\n");
+ SV_INIT.svs.realtime = SV_INIT.sv.time;
+ }
+ }
+
+ if (Globals.host_speeds.value != 0)
+ Globals.time_after_game = Sys.Milliseconds();
+
+ }
+
+ /*
+ * ================== SV_Frame
+ *
+ * ==================
+ */
+ public static void SV_Frame(long msec) {
+ Globals.time_before_game = Globals.time_after_game = 0;
+
+ // if server is not active, do nothing
+ if (!SV_INIT.svs.initialized)
+ return;
+
+ SV_INIT.svs.realtime += msec;
+
+ // keep the random time dependent
+ Lib.rand();
+
+ // check timeouts
+ SV_CheckTimeouts();
+
+ // get packets from clients
+ SV_ReadPackets();
+
+ //if (Game.g_edicts[1] !=null)
+ // Com.p("player at:" + Lib.vtofsbeaty(Game.g_edicts[1].s.origin ));
+
+ // move autonomous things around if enough time has passed
+ if (0 == SV_MAIN.sv_timedemo.value
+ && SV_INIT.svs.realtime < SV_INIT.sv.time) {
+ // never let the time get too far off
+ if (SV_INIT.sv.time - SV_INIT.svs.realtime > 100) {
+ if (SV_MAIN.sv_showclamp.value != 0)
+ Com.Printf("sv lowclamp\n");
+ SV_INIT.svs.realtime = SV_INIT.sv.time - 100;
+ }
+ NET.NET_Sleep(SV_INIT.sv.time - SV_INIT.svs.realtime);
+ return;
+ }
+
+ // update ping based on the last known frame from all clients
+ //TODO: dont need yet
+ SV_CalcPings();
+
+ // give the clients some timeslices
+ //TODO: dont need yet
+ SV_GiveMsec();
+
+ // let everything in the world think and move
+ SV_RunGameFrame();
+
+ // send messages back to the clients that had packets read this frame
+ SV_SEND.SV_SendClientMessages();
+
+ // save the entire world state if recording a serverdemo
+ //TODO: dont need yet
+ //SV_WORLD.SV_RecordDemoMessage();
+
+ // send a heartbeat to the master if needed
+ //TODO: dont need yet
+ Master_Heartbeat();
+
+ // clear teleport flags, etc for next frame
+ SV_PrepWorldFrame();
+
+ }
+
+ public static void Master_Heartbeat() {
+ String string;
+ int i;
+
+ // pgm post3.19 change, cvar pointer not validated before dereferencing
+ if (Globals.dedicated == null || 0 == Globals.dedicated.value)
+ return; // only dedicated servers send heartbeats
+
+ // pgm post3.19 change, cvar pointer not validated before dereferencing
+ if (null == SV_MAIN.public_server || 0 == SV_MAIN.public_server.value)
+ return; // a private dedicated game
+
+ // check for time wraparound
+ if (SV_INIT.svs.last_heartbeat > SV_INIT.svs.realtime)
+ SV_INIT.svs.last_heartbeat = SV_INIT.svs.realtime;
+
+ if (SV_INIT.svs.realtime - SV_INIT.svs.last_heartbeat < SV_MAIN.HEARTBEAT_SECONDS * 1000)
+ return; // not time to send yet
+
+ SV_INIT.svs.last_heartbeat = SV_INIT.svs.realtime;
+
+ // send the same string that we would give for a status OOB command
+ string = SV_StatusString();
+
+ // send to group master
+ for (i = 0; i < Defines.MAX_MASTERS; i++)
+ if (SV_MAIN.master_adr[i].port != 0) {
+ Com.Printf("Sending heartbeat to "
+ + NET.AdrToString(SV_MAIN.master_adr[i]) + "\n");
+ Netchan.OutOfBandPrint(Defines.NS_SERVER,
+ SV_MAIN.master_adr[i], "heartbeat\n" + string);
+ }
+ }
+
+ /*
+ * ================= Master_Shutdown
+ *
+ * Informs all masters that this server is going down =================
+ */
+ public static void Master_Shutdown() {
+ int i;
+
+ // pgm post3.19 change, cvar pointer not validated before dereferencing
+ if (null == Globals.dedicated || 0 == Globals.dedicated.value)
+ return; // only dedicated servers send heartbeats
+
+ // pgm post3.19 change, cvar pointer not validated before dereferencing
+ if (null == SV_MAIN.public_server || 0 == SV_MAIN.public_server.value)
+ return; // a private dedicated game
+
+ // send to group master
+ for (i = 0; i < Defines.MAX_MASTERS; i++)
+ if (SV_MAIN.master_adr[i].port != 0) {
+ if (i > 0)
+ Com.Printf("Sending heartbeat to "
+ + NET.AdrToString(SV_MAIN.master_adr[i]) + "\n");
+ Netchan.OutOfBandPrint(Defines.NS_SERVER,
+ SV_MAIN.master_adr[i], "shutdown");
+ }
+ }
+
+ //============================================================================
+
+ /*
+ * ================= SV_UserinfoChanged
+ *
+ * Pull specific info from a newly changed userinfo string into a more C
+ * freindly form. =================
+ */
+ public static void SV_UserinfoChanged(client_t cl) {
+ String val;
+ int i;
+
+ // call prog code to allow overrides
+ PlayerClient.ClientUserinfoChanged(cl.edict, cl.userinfo);
+
+ // name for C code
+ cl.name = Info.Info_ValueForKey(cl.userinfo, "name");
+
+ // mask off high bit
+ //TODO: masking for german umlaute
+ //for (i=0 ; i<sizeof(cl.name) ; i++)
+ // cl.name[i] &= 127;
+
+ // rate command
+ val = Info.Info_ValueForKey(cl.userinfo, "rate");
+ if (val.length() > 0) {
+ i = Lib.atoi(val);
+ cl.rate = i;
+ if (cl.rate < 100)
+ cl.rate = 100;
+ if (cl.rate > 15000)
+ cl.rate = 15000;
+ } else
+ cl.rate = 5000;
+
+ // msg command
+ val = Info.Info_ValueForKey(cl.userinfo, "msg");
+ if (val.length() > 0) {
+ cl.messagelevel = Lib.atoi(val);
+ }
+
+ }
+
+ //============================================================================
+
+ /*
+ * =============== SV_Init
+ *
+ * Only called at quake2.exe startup, not for each game ===============
+ */
+ public static void SV_Init() {
+ SV_CCMDS.SV_InitOperatorCommands(); //ok.
+
+ SV_MAIN.rcon_password = Cvar.Get("rcon_password", "", 0);
+ Cvar.Get("skill", "1", 0);
+ Cvar.Get("deathmatch", "0", Defines.CVAR_LATCH);
+ Cvar.Get("coop", "0", Defines.CVAR_LATCH);
+ Cvar.Get("dmflags", "" + Defines.DF_INSTANT_ITEMS,
+ Defines.CVAR_SERVERINFO);
+ Cvar.Get("fraglimit", "0", Defines.CVAR_SERVERINFO);
+ Cvar.Get("timelimit", "0", Defines.CVAR_SERVERINFO);
+ //TODO: set cheats 0
+ Cvar.Get("cheats", "1", Defines.CVAR_SERVERINFO | Defines.CVAR_LATCH);
+ Cvar.Get("protocol", "" + Defines.PROTOCOL_VERSION,
+ Defines.CVAR_SERVERINFO | Defines.CVAR_NOSET);
+
+ SV_MAIN.maxclients = Cvar.Get("maxclients", "1",
+ Defines.CVAR_SERVERINFO | Defines.CVAR_LATCH);
+ SV_MAIN.hostname = Cvar.Get("hostname", "noname",
+ Defines.CVAR_SERVERINFO | Defines.CVAR_ARCHIVE);
+ SV_MAIN.timeout = Cvar.Get("timeout", "125", 0);
+ SV_MAIN.zombietime = Cvar.Get("zombietime", "2", 0);
+ SV_MAIN.sv_showclamp = Cvar.Get("showclamp", "0", 0);
+ SV_MAIN.sv_paused = Cvar.Get("paused", "0", 0);
+ SV_MAIN.sv_timedemo = Cvar.Get("timedemo", "0", 0);
+ SV_MAIN.sv_enforcetime = Cvar.Get("sv_enforcetime", "0", 0);
+
+ // TODO: carsten, re-allow downloads per default
+ SV_MAIN.allow_download = Cvar.Get("allow_download", "0",
+ Defines.CVAR_ARCHIVE);
+ SV_MAIN.allow_download_players = Cvar.Get("allow_download_players",
+ "0", Defines.CVAR_ARCHIVE);
+ SV_MAIN.allow_download_models = Cvar.Get("allow_download_models", "1",
+ Defines.CVAR_ARCHIVE);
+ SV_MAIN.allow_download_sounds = Cvar.Get("allow_download_sounds", "1",
+ Defines.CVAR_ARCHIVE);
+ SV_MAIN.allow_download_maps = Cvar.Get("allow_download_maps", "1",
+ Defines.CVAR_ARCHIVE);
+
+ SV_MAIN.sv_noreload = Cvar.Get("sv_noreload", "0", 0);
+ SV_MAIN.sv_airaccelerate = Cvar.Get("sv_airaccelerate", "0",
+ Defines.CVAR_LATCH);
+ SV_MAIN.public_server = Cvar.Get("public", "0", 0);
+ SV_MAIN.sv_reconnect_limit = Cvar.Get("sv_reconnect_limit", "3",
+ Defines.CVAR_ARCHIVE);
+
+ SZ.Init(Globals.net_message, Globals.net_message_buffer,
+ Globals.net_message_buffer.length);
+ }
+
+ /*
+ * ================== SV_FinalMessage
+ *
+ * Used by SV_Shutdown to send a final message to all connected clients
+ * before the server goes down. The messages are sent immediately, not just
+ * stuck on the outgoing message list, because the server is going to
+ * totally exit after returning from this function. ==================
+ */
+ public static void SV_FinalMessage(String message, boolean reconnect) {
+ int i;
+ client_t cl;
+
+ SZ.Clear(Globals.net_message);
+ MSG.WriteByte(Globals.net_message, Defines.svc_print);
+ MSG.WriteByte(Globals.net_message, Defines.PRINT_HIGH);
+ MSG.WriteString(Globals.net_message, message);
+
+ if (reconnect)
+ MSG.WriteByte(Globals.net_message, Defines.svc_reconnect);
+ else
+ MSG.WriteByte(Globals.net_message, Defines.svc_disconnect);
+
+ // send it twice
+ // stagger the packets to crutch operating system limited buffers
+
+ for (i = 0; i < SV_MAIN.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (cl.state >= Defines.cs_connected)
+ Netchan.Transmit(cl.netchan, Globals.net_message.cursize,
+ Globals.net_message.data);
+ }
+ for (i = 0; i < SV_MAIN.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (cl.state >= Defines.cs_connected)
+ Netchan.Transmit(cl.netchan, Globals.net_message.cursize,
+ Globals.net_message.data);
+ }
+ }
+
+ /*
+ * ================ SV_Shutdown
+ *
+ * Called when each game quits, before Sys_Quit or Sys_Error
+ * ================
+ */
+ public static void SV_Shutdown(String finalmsg, boolean reconnect) {
+ if (SV_INIT.svs.clients != null)
+ SV_FinalMessage(finalmsg, reconnect);
+
+ Master_Shutdown();
+
+ SV_GAME.SV_ShutdownGameProgs();
+
+ // free current level
+ if (SV_INIT.sv.demofile != null)
+ try {
+ SV_INIT.sv.demofile.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ SV_INIT.sv = new server_t();
+
+ Globals.server_state = SV_INIT.sv.state;
+
+ if (SV_INIT.svs.demofile != null)
+ try {
+ SV_INIT.svs.demofile.close();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+
+ SV_INIT.svs = new server_static_t();
+ }
+} \ No newline at end of file
diff --git a/src/jake2/server/SV_SEND.java b/src/jake2/server/SV_SEND.java
index 9b30a99..8488954 100644
--- a/src/jake2/server/SV_SEND.java
+++ b/src/jake2/server/SV_SEND.java
@@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// Created on 17.01.2004 by RST.
-// $Id: SV_SEND.java,v 1.5 2004-09-10 19:02:56 salomo Exp $
+// $Id: SV_SEND.java,v 1.6 2004-09-22 19:22:12 salomo Exp $
package jake2.server;
@@ -30,8 +30,9 @@ import jake2.client.*;
import jake2.game.*;
import jake2.qcommon.*;
import jake2.render.*;
+import jake2.util.Math3D;
-public class SV_SEND extends SV_MAIN {
+public class SV_SEND {
/*
=============================================================================
@@ -40,20 +41,19 @@ public class SV_SEND extends SV_MAIN {
=============================================================================
*/
- public static byte sv_outputbuf[]= new byte[SV_OUTPUTBUF_LENGTH];
+ public static byte sv_outputbuf[] = new byte[Defines.SV_OUTPUTBUF_LENGTH];
public static void SV_FlushRedirect(int sv_redirected, byte outputbuf[]) {
- if (sv_redirected == RD_PACKET) {
- String s= ("print\n" + outputbuf);
- Netchan.Netchan_OutOfBand(NS_SERVER, Netchan.net_from, s.length(), s.getBytes());
+ if (sv_redirected == Defines.RD_PACKET) {
+ String s = ("print\n" + outputbuf);
+ Netchan.Netchan_OutOfBand(Defines.NS_SERVER, Globals.net_from, s.length(), s.getBytes());
}
- else if (sv_redirected == RD_CLIENT) {
- MSG.WriteByte(SV_MAIN.sv_client.netchan.message, svc_print);
- MSG.WriteByte(SV_MAIN.sv_client.netchan.message, PRINT_HIGH);
+ else if (sv_redirected == Defines.RD_CLIENT) {
+ MSG.WriteByte(SV_MAIN.sv_client.netchan.message, Defines.svc_print);
+ MSG.WriteByte(SV_MAIN.sv_client.netchan.message, Defines.PRINT_HIGH);
MSG.WriteString(SV_MAIN.sv_client.netchan.message, outputbuf);
- }
+ }
}
-
/*
=============================================================================
@@ -74,11 +74,10 @@ public class SV_SEND extends SV_MAIN {
if (level < cl.messagelevel)
return;
- MSG.WriteByte(cl.netchan.message, svc_print);
+ MSG.WriteByte(cl.netchan.message, Defines.svc_print);
MSG.WriteByte(cl.netchan.message, level);
MSG.WriteString(cl.netchan.message, s);
}
-
/*
=================
SV_BroadcastPrintf
@@ -91,23 +90,22 @@ public class SV_SEND extends SV_MAIN {
client_t cl;
// echo to console
- if (dedicated.value != 0) {
+ if (Globals.dedicated.value != 0) {
Com.Printf(s);
}
- for (int i= 0; i < SV_MAIN.maxclients.value; i++) {
- cl= SV_MAIN.svs.clients[i];
+ for (int i = 0; i < SV_MAIN.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
if (level < cl.messagelevel)
continue;
- if (cl.state != cs_spawned)
+ if (cl.state != Defines.cs_spawned)
continue;
- MSG.WriteByte(cl.netchan.message, svc_print);
+ MSG.WriteByte(cl.netchan.message, Defines.svc_print);
MSG.WriteByte(cl.netchan.message, level);
MSG.WriteString(cl.netchan.message, s);
}
}
-
/*
=================
SV_BroadcastCommand
@@ -117,14 +115,13 @@ public class SV_SEND extends SV_MAIN {
*/
public static void SV_BroadcastCommand(String s) {
- if (sv.state == 0)
+ if (SV_INIT.sv.state == 0)
return;
- MSG.WriteByte(sv.multicast, svc_stufftext);
- MSG.WriteString(sv.multicast, s);
- SV_Multicast(null, MULTICAST_ALL_R);
+ MSG.WriteByte(SV_INIT.sv.multicast, Defines.svc_stufftext);
+ MSG.WriteString(SV_INIT.sv.multicast, s);
+ SV_Multicast(null, Defines.MULTICAST_ALL_R);
}
-
/*
=================
SV_Multicast
@@ -145,63 +142,63 @@ public class SV_SEND extends SV_MAIN {
boolean reliable;
int area1, area2;
- reliable= false;
+ reliable = false;
- if (to != MULTICAST_ALL_R && to != MULTICAST_ALL) {
- leafnum= CM.CM_PointLeafnum(origin);
- area1= CM.CM_LeafArea(leafnum);
+ if (to != Defines.MULTICAST_ALL_R && to != Defines.MULTICAST_ALL) {
+ leafnum = CM.CM_PointLeafnum(origin);
+ area1 = CM.CM_LeafArea(leafnum);
}
else {
- leafnum= 0; // just to avoid compiler warnings
- area1= 0;
+ leafnum = 0; // just to avoid compiler warnings
+ area1 = 0;
}
// if doing a serverrecord, store everything
- if (svs.demofile != null)
- SZ.Write(svs.demo_multicast, sv.multicast.data, sv.multicast.cursize);
+ if (SV_INIT.svs.demofile != null)
+ SZ.Write(SV_INIT.svs.demo_multicast, SV_INIT.sv.multicast.data, SV_INIT.sv.multicast.cursize);
switch (to) {
- case MULTICAST_ALL_R :
- reliable= true; // intentional fallthrough, no break here
- case MULTICAST_ALL :
- leafnum= 0;
- mask= null;
+ case Defines.MULTICAST_ALL_R :
+ reliable = true; // intentional fallthrough, no break here
+ case Defines.MULTICAST_ALL :
+ leafnum = 0;
+ mask = null;
break;
- case MULTICAST_PHS_R :
- reliable= true; // intentional fallthrough
- case MULTICAST_PHS :
- leafnum= CM.CM_PointLeafnum(origin);
- cluster= CM.CM_LeafCluster(leafnum);
- mask= CM.CM_ClusterPHS(cluster);
+ case Defines.MULTICAST_PHS_R :
+ reliable = true; // intentional fallthrough
+ case Defines.MULTICAST_PHS :
+ leafnum = CM.CM_PointLeafnum(origin);
+ cluster = CM.CM_LeafCluster(leafnum);
+ mask = CM.CM_ClusterPHS(cluster);
break;
- case MULTICAST_PVS_R :
- reliable= true; // intentional fallthrough
- case MULTICAST_PVS :
- leafnum= CM.CM_PointLeafnum(origin);
- cluster= CM.CM_LeafCluster(leafnum);
- mask= CM.CM_ClusterPVS(cluster);
+ case Defines.MULTICAST_PVS_R :
+ reliable = true; // intentional fallthrough
+ case Defines.MULTICAST_PVS :
+ leafnum = CM.CM_PointLeafnum(origin);
+ cluster = CM.CM_LeafCluster(leafnum);
+ mask = CM.CM_ClusterPVS(cluster);
break;
default :
- mask= null;
- Com.Error(ERR_FATAL, "SV_Multicast: bad to:" + to + "\n");
+ mask = null;
+ Com.Error(Defines.ERR_FATAL, "SV_Multicast: bad to:" + to + "\n");
}
// send the data to all relevent clients
- for (j= 0; j < maxclients.value; j++) {
- client= svs.clients[j];
+ for (j = 0; j < SV_MAIN.maxclients.value; j++) {
+ client = SV_INIT.svs.clients[j];
- if (client.state == cs_free || client.state == cs_zombie)
+ if (client.state == Defines.cs_free || client.state == Defines.cs_zombie)
continue;
- if (client.state != cs_spawned && !reliable)
+ if (client.state != Defines.cs_spawned && !reliable)
continue;
if (mask != null) {
- leafnum= CM.CM_PointLeafnum(client.edict.s.origin);
- cluster= CM.CM_LeafCluster(leafnum);
- area2= CM.CM_LeafArea(leafnum);
+ leafnum = CM.CM_PointLeafnum(client.edict.s.origin);
+ cluster = CM.CM_LeafCluster(leafnum);
+ area2 = CM.CM_LeafArea(leafnum);
if (!CM.CM_AreasConnected(area1, area2))
continue;
@@ -213,14 +210,13 @@ public class SV_SEND extends SV_MAIN {
}
if (reliable)
- SZ.Write(client.netchan.message, sv.multicast.data, sv.multicast.cursize);
+ SZ.Write(client.netchan.message, SV_INIT.sv.multicast.data, SV_INIT.sv.multicast.cursize);
else
- SZ.Write(client.datagram, sv.multicast.data, sv.multicast.cursize);
+ SZ.Write(client.datagram, SV_INIT.sv.multicast.data, SV_INIT.sv.multicast.cursize);
}
- SZ.Clear(sv.multicast);
+ SZ.Clear(SV_INIT.sv.multicast);
}
-
/*
==================
SV_StartSound
@@ -259,98 +255,97 @@ public class SV_SEND extends SV_MAIN {
int flags;
int i;
int ent;
- float[] origin_v= { 0, 0, 0 };
+ float[] origin_v = { 0, 0, 0 };
boolean use_phs;
if (volume < 0 || volume > 1.0)
- Com.Error(ERR_FATAL, "SV_StartSound: volume = " + volume);
+ Com.Error(Defines.ERR_FATAL, "SV_StartSound: volume = " + volume);
if (attenuation < 0 || attenuation > 4)
- Com.Error(ERR_FATAL, "SV_StartSound: attenuation = " + attenuation);
+ Com.Error(Defines.ERR_FATAL, "SV_StartSound: attenuation = " + attenuation);
// if (channel < 0 || channel > 15)
// Com_Error (ERR_FATAL, "SV_StartSound: channel = %i", channel);
if (timeofs < 0 || timeofs > 0.255)
- Com.Error(ERR_FATAL, "SV_StartSound: timeofs = " + timeofs);
+ Com.Error(Defines.ERR_FATAL, "SV_StartSound: timeofs = " + timeofs);
- ent= entity.index;
+ ent = entity.index;
// no PHS flag
if ((channel & 8) != 0) {
- use_phs= false;
+ use_phs = false;
channel &= 7;
}
else
- use_phs= true;
+ use_phs = true;
- sendchan= (ent << 3) | (channel & 7);
+ sendchan = (ent << 3) | (channel & 7);
- flags= 0;
+ flags = 0;
if (volume != Defines.DEFAULT_SOUND_PACKET_VOLUME)
- flags |= SND_VOLUME;
- if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
- flags |= SND_ATTENUATION;
+ flags |= Defines.SND_VOLUME;
+ if (attenuation != Defines.DEFAULT_SOUND_PACKET_ATTENUATION)
+ flags |= Defines.SND_ATTENUATION;
// the client doesn't know that bmodels have weird origins
// the origin can also be explicitly set
- if ((entity.svflags & SVF_NOCLIENT) != 0 || (entity.solid == SOLID_BSP) || origin != null)
- flags |= SND_POS;
+ if ((entity.svflags & Defines.SVF_NOCLIENT) != 0 || (entity.solid == Defines.SOLID_BSP) || origin != null)
+ flags |= Defines.SND_POS;
// always send the entity number for channel overrides
- flags |= SND_ENT;
+ flags |= Defines.SND_ENT;
if (timeofs != 0)
- flags |= SND_OFFSET;
+ flags |= Defines.SND_OFFSET;
// use the entity origin unless it is a bmodel or explicitly specified
if (origin == null) {
- origin= origin_v;
- if (entity.solid == SOLID_BSP) {
- for (i= 0; i < 3; i++)
- origin_v[i]= entity.s.origin[i] + 0.5f * (entity.mins[i] + entity.maxs[i]);
+ origin = origin_v;
+ if (entity.solid == Defines.SOLID_BSP) {
+ for (i = 0; i < 3; i++)
+ origin_v[i] = entity.s.origin[i] + 0.5f * (entity.mins[i] + entity.maxs[i]);
}
else {
- VectorCopy(entity.s.origin, origin_v);
+ Math3D.VectorCopy(entity.s.origin, origin_v);
}
}
- MSG.WriteByte(sv.multicast, svc_sound);
- MSG.WriteByte(sv.multicast, flags);
- MSG.WriteByte(sv.multicast, soundindex);
+ MSG.WriteByte(SV_INIT.sv.multicast, Defines.svc_sound);
+ MSG.WriteByte(SV_INIT.sv.multicast, flags);
+ MSG.WriteByte(SV_INIT.sv.multicast, soundindex);
- if ((flags & SND_VOLUME) != 0)
- MSG.WriteByte(sv.multicast, volume * 255);
- if ((flags & SND_ATTENUATION) != 0)
- MSG.WriteByte(sv.multicast, attenuation * 64);
- if ((flags & SND_OFFSET) != 0)
- MSG.WriteByte(sv.multicast, timeofs * 1000);
+ if ((flags & Defines.SND_VOLUME) != 0)
+ MSG.WriteByte(SV_INIT.sv.multicast, volume * 255);
+ if ((flags & Defines.SND_ATTENUATION) != 0)
+ MSG.WriteByte(SV_INIT.sv.multicast, attenuation * 64);
+ if ((flags & Defines.SND_OFFSET) != 0)
+ MSG.WriteByte(SV_INIT.sv.multicast, timeofs * 1000);
- if ((flags & SND_ENT) != 0)
- MSG.WriteShort(sv.multicast, sendchan);
+ if ((flags & Defines.SND_ENT) != 0)
+ MSG.WriteShort(SV_INIT.sv.multicast, sendchan);
- if ((flags & SND_POS) != 0)
- MSG.WritePos(sv.multicast, origin);
+ if ((flags & Defines.SND_POS) != 0)
+ MSG.WritePos(SV_INIT.sv.multicast, origin);
// if the sound doesn't attenuate,send it to everyone
// (global radio chatter, voiceovers, etc)
- if (attenuation == ATTN_NONE)
- use_phs= false;
+ if (attenuation == Defines.ATTN_NONE)
+ use_phs = false;
- if ((channel & CHAN_RELIABLE) != 0) {
+ if ((channel & Defines.CHAN_RELIABLE) != 0) {
if (use_phs)
- SV_Multicast(origin, MULTICAST_PHS_R);
+ SV_Multicast(origin, Defines.MULTICAST_PHS_R);
else
- SV_Multicast(origin, MULTICAST_ALL_R);
+ SV_Multicast(origin, Defines.MULTICAST_ALL_R);
}
else {
if (use_phs)
- SV_Multicast(origin, MULTICAST_PHS);
+ SV_Multicast(origin, Defines.MULTICAST_PHS);
else
- SV_Multicast(origin, MULTICAST_ALL);
+ SV_Multicast(origin, Defines.MULTICAST_ALL);
}
}
-
/*
===============================================================================
@@ -365,17 +360,17 @@ public class SV_SEND extends SV_MAIN {
=======================
*/
public static boolean SV_SendClientDatagram(client_t client) {
- byte msg_buf[]= new byte[MAX_MSGLEN];
- sizebuf_t msg= new sizebuf_t();
+ byte msg_buf[] = new byte[Defines.MAX_MSGLEN];
+ sizebuf_t msg = new sizebuf_t();
SV_ENTS.SV_BuildClientFrame(client);
SZ.Init(msg, msg_buf, msg_buf.length);
- msg.allowoverflow= true;
+ msg.allowoverflow = true;
// send over all the relevant entity_state_t
// and the player_state_t
- SV_CCMDS.SV_WriteFrameToClient(client, msg);
+ SV_ENTS.SV_WriteFrameToClient(client, msg);
// copy the accumulated multicast datagram
// for this client out to the message
@@ -396,29 +391,27 @@ public class SV_SEND extends SV_MAIN {
Netchan.Transmit(client.netchan, msg.cursize, msg.data);
// record the size for rate estimation
- client.message_size[sv.framenum % RATE_MESSAGES]= msg.cursize;
+ client.message_size[SV_INIT.sv.framenum % Defines.RATE_MESSAGES] = msg.cursize;
return true;
}
-
/*
==================
SV_DemoCompleted
==================
*/
public static void SV_DemoCompleted() {
- if (sv.demofile != null) {
+ if (SV_INIT.sv.demofile != null) {
try {
- sv.demofile.close();
+ SV_INIT.sv.demofile.close();
}
catch (IOException e) {
Com.Printf("IOError closing d9emo fiele:" + e);
}
- sv.demofile= null;
+ SV_INIT.sv.demofile = null;
}
- SV_ENTS.SV_Nextserver();
+ SV_USER.SV_Nextserver();
}
-
/*
=======================
SV_RateDrop
@@ -432,24 +425,23 @@ public class SV_SEND extends SV_MAIN {
int i;
// never drop over the loopback
- if (c.netchan.remote_address.type == NA_LOOPBACK)
+ if (c.netchan.remote_address.type == Defines.NA_LOOPBACK)
return false;
- total= 0;
+ total = 0;
- for (i= 0; i < RATE_MESSAGES; i++) {
+ for (i = 0; i < Defines.RATE_MESSAGES; i++) {
total += c.message_size[i];
}
if (total > c.rate) {
c.surpressCount++;
- c.message_size[sv.framenum % RATE_MESSAGES]= 0;
+ c.message_size[SV_INIT.sv.framenum % Defines.RATE_MESSAGES] = 0;
return true;
}
return false;
}
-
/*
=======================
SV_SendClientMessages
@@ -459,20 +451,20 @@ public class SV_SEND extends SV_MAIN {
int i;
client_t c;
int msglen;
- byte msgbuf[]= new byte[MAX_MSGLEN];
+ byte msgbuf[] = new byte[Defines.MAX_MSGLEN];
int r;
- msglen= 0;
+ msglen = 0;
// read the next demo message if needed
- if (sv.state == ss_demo && sv.demofile != null) {
- if (sv_paused.value != 0)
- msglen= 0;
+ if (SV_INIT.sv.state == Defines.ss_demo && SV_INIT.sv.demofile != null) {
+ if (SV_MAIN.sv_paused.value != 0)
+ msglen = 0;
else {
// get the next message
//r = fread (&msglen, 4, 1, sv.demofile);
try {
- msglen= EndianHandler.swapInt(sv.demofile.readInt());
+ msglen = EndianHandler.swapInt(SV_INIT.sv.demofile.readInt());
}
catch (Exception e) {
SV_DemoCompleted();
@@ -484,13 +476,13 @@ public class SV_SEND extends SV_MAIN {
SV_DemoCompleted();
return;
}
- if (msglen > MAX_MSGLEN)
- Com.Error(ERR_DROP, "SV_SendClientMessages: msglen > MAX_MSGLEN");
+ if (msglen > Defines.MAX_MSGLEN)
+ Com.Error(Defines.ERR_DROP, "SV_SendClientMessages: msglen > MAX_MSGLEN");
//r = fread (msgbuf, msglen, 1, sv.demofile);
- r= 0;
+ r = 0;
try {
- r= sv.demofile.read(msgbuf, 0, msglen);
+ r = SV_INIT.sv.demofile.read(msgbuf, 0, msglen);
}
catch (IOException e1) {
Com.Printf("IOError: reading demo file, " + e1);
@@ -503,8 +495,8 @@ public class SV_SEND extends SV_MAIN {
}
// send a message to each connected client
- for (i= 0; i < maxclients.value; i++) {
- c= svs.clients[i];
+ for (i = 0; i < SV_MAIN.maxclients.value; i++) {
+ c = SV_INIT.svs.clients[i];
if (c.state == 0)
continue;
@@ -513,13 +505,15 @@ public class SV_SEND extends SV_MAIN {
if (c.netchan.message.overflowed) {
SZ.Clear(c.netchan.message);
SZ.Clear(c.datagram);
- SV_BroadcastPrintf(PRINT_HIGH, c.name + " overflowed\n");
- SV_DropClient(c);
+ SV_BroadcastPrintf(Defines.PRINT_HIGH, c.name + " overflowed\n");
+ SV_MAIN.SV_DropClient(c);
}
- if (sv.state == ss_cinematic || sv.state == ss_demo || sv.state == ss_pic)
+ if (SV_INIT.sv.state == Defines.ss_cinematic
+ || SV_INIT.sv.state == Defines.ss_demo
+ || SV_INIT.sv.state == Defines.ss_pic)
Netchan.Transmit(c.netchan, msglen, msgbuf);
- else if (c.state == cs_spawned) {
+ else if (c.state == Defines.cs_spawned) {
// don't overrun bandwidth
if (SV_RateDrop(c))
continue;
diff --git a/src/jake2/server/SV_USER.java b/src/jake2/server/SV_USER.java
index 5a31de8..c03cacf 100644
--- a/src/jake2/server/SV_USER.java
+++ b/src/jake2/server/SV_USER.java
@@ -1,735 +1,691 @@
/*
-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_USER.java,v 1.5 2004-08-29 21:39:25 hzi Exp $
-
+// $Id: SV_USER.java,v 1.6 2004-09-22 19:22:12 salomo Exp $
package jake2.server;
-import jake2.game.*;
-import jake2.qcommon.*;
+import jake2.Defines;
+import jake2.Globals;
+import jake2.game.Cmd;
+import jake2.game.GameAI;
+import jake2.game.GameBase;
+import jake2.game.Info;
+import jake2.game.PlayerClient;
+import jake2.game.edict_t;
+import jake2.game.entity_state_t;
+import jake2.game.usercmd_t;
+import jake2.qcommon.Cbuf;
+import jake2.qcommon.Com;
+import jake2.qcommon.Cvar;
+import jake2.qcommon.FS;
+import jake2.qcommon.MSG;
+import jake2.qcommon.SZ;
import jake2.util.Lib;
import java.io.IOException;
-public class SV_USER extends SV_SEND
-{
-
- static edict_t sv_player;
-
- /*
- ============================================================
-
- USER STRINGCMD EXECUTION
-
- sv_client and sv_player will be valid.
- ============================================================
- */
-
- /*
- ==================
- SV_BeginDemoServer
- ==================
- */
- public static void SV_BeginDemoserver()
- {
- String name;
-
- name= "demos/" + sv.name;
- try
- {
- sv.demofile= FS.FOpenFile(name);
- }
- catch (IOException e)
- {
- Com.Error(ERR_DROP, "Couldn't open " + name + "\n");
- }
- if (sv.demofile == null)
- Com.Error(ERR_DROP, "Couldn't open " + name + "\n");
- }
-
- /*
- ================
- SV_New_f
-
- Sends the first message from the server to a connected client.
- This will be sent on the initial connection and upon each server load.
- ================
- */
- public static void SV_New_f()
- {
- String gamedir;
- int playernum;
- edict_t ent;
-
- Com.DPrintf("New() from " + sv_client.name + "\n");
-
- if (sv_client.state != cs_connected)
- {
- Com.Printf("New not valid -- already spawned\n");
- return;
- }
-
- // demo servers just dump the file message
- if (sv.state == ss_demo)
- {
- SV_BeginDemoserver();
- return;
- }
-
- //
- // serverdata needs to go over for all types of servers
- // to make sure the protocol is right, and to set the gamedir
- //
- gamedir= Cvar.VariableString("gamedir");
-
- // send the serverdata
- MSG.WriteByte(sv_client.netchan.message, svc_serverdata);
- MSG.WriteInt(sv_client.netchan.message, PROTOCOL_VERSION);
- MSG.WriteLong(sv_client.netchan.message, svs.spawncount);
- MSG.WriteByte(sv_client.netchan.message, sv.attractloop ? 1 : 0);
- MSG.WriteString(sv_client.netchan.message, gamedir);
-
- if (sv.state == ss_cinematic || sv.state == ss_pic)
- playernum= -1;
- else
- //playernum = sv_client - svs.clients;
- playernum= sv_client.serverindex;
-
- MSG.WriteShort(sv_client.netchan.message, playernum);
-
- // send full levelname
- MSG.WriteString(sv_client.netchan.message, sv.configstrings[CS_NAME]);
-
- //
- // game server
- //
- if (sv.state == ss_game)
- {
- // set up the entity for the client
- ent= GameBase.g_edicts[playernum + 1];
- ent.s.number= playernum + 1;
- sv_client.edict= ent;
- sv_client.lastcmd= new usercmd_t();
-
- // begin fetching configstrings
- MSG.WriteByte(sv_client.netchan.message, svc_stufftext);
- MSG.WriteString(sv_client.netchan.message, "cmd configstrings " + svs.spawncount + " 0\n");
- }
-
- }
-
- /*
- ==================
- SV_Configstrings_f
- ==================
- */
- public static void SV_Configstrings_f()
- {
- int start;
-
- Com.DPrintf("Configstrings() from " + sv_client.name + "\n");
-
- if (sv_client.state != cs_connected)
- {
- Com.Printf("configstrings not valid -- already spawned\n");
- return;
- }
-
- // handle the case of a level changing while a client was connecting
- if (atoi(Cmd.Argv(1)) != svs.spawncount)
- {
- Com.Printf("SV_Configstrings_f from different level\n");
- SV_New_f();
- return;
- }
-
- start= atoi(Cmd.Argv(2));
-
- // write a packet full of data
-
- while (sv_client.netchan.message.cursize < MAX_MSGLEN / 2 && start < MAX_CONFIGSTRINGS)
- {
- if (sv.configstrings[start] != null && sv.configstrings[start].length() != 0)
- {
- MSG.WriteByte(sv_client.netchan.message, svc_configstring);
- MSG.WriteShort(sv_client.netchan.message, start);
- MSG.WriteString(sv_client.netchan.message, sv.configstrings[start]);
- }
- start++;
- }
-
- // send next command
-
- if (start == MAX_CONFIGSTRINGS)
- {
- MSG.WriteByte(sv_client.netchan.message, svc_stufftext);
- MSG.WriteString(sv_client.netchan.message, "cmd baselines " + svs.spawncount + " 0\n");
- }
- else
- {
- MSG.WriteByte(sv_client.netchan.message, svc_stufftext);
- MSG.WriteString(sv_client.netchan.message, "cmd configstrings " + svs.spawncount + " " + start + "\n");
- }
- }
-
- /*
- ==================
- SV_Baselines_f
- ==================
- */
- public static void SV_Baselines_f()
- {
- int start;
- entity_state_t nullstate;
- entity_state_t base;
-
- Com.DPrintf("Baselines() from " + sv_client.name + "\n");
-
- if (sv_client.state != cs_connected)
- {
- Com.Printf("baselines not valid -- already spawned\n");
- return;
- }
-
- // handle the case of a level changing while a client was connecting
- if (atoi(Cmd.Argv(1)) != svs.spawncount)
- {
- Com.Printf("SV_Baselines_f from different level\n");
- SV_New_f();
- return;
- }
-
- start= atoi(Cmd.Argv(2));
-
- //memset (&nullstate, 0, sizeof(nullstate));
- nullstate= new entity_state_t(null);
-
- // write a packet full of data
-
- while (sv_client.netchan.message.cursize < MAX_MSGLEN / 2 && start < MAX_EDICTS)
- {
- base= sv.baselines[start];
- if (base.modelindex != 0 || base.sound != 0 || base.effects != 0)
- {
- MSG.WriteByte(sv_client.netchan.message, svc_spawnbaseline);
- MSG.WriteDeltaEntity(nullstate, base, sv_client.netchan.message, true, true);
- }
- start++;
- }
-
- // send next command
-
- if (start == MAX_EDICTS)
- {
- MSG.WriteByte(sv_client.netchan.message, svc_stufftext);
- MSG.WriteString(sv_client.netchan.message, "precache " + svs.spawncount + "\n");
- }
- else
- {
- MSG.WriteByte(sv_client.netchan.message, svc_stufftext);
- MSG.WriteString(sv_client.netchan.message, "cmd baselines " + svs.spawncount + " " + start + "\n");
- }
- }
-
- /*
- ==================
- SV_Begin_f
- ==================
- */
- public static void SV_Begin_f()
- {
- Com.DPrintf("Begin() from " + sv_client.name + "\n");
-
- // handle the case of a level changing while a client was connecting
- if (atoi(Cmd.Argv(1)) != svs.spawncount)
- {
- Com.Printf("SV_Begin_f from different level\n");
- SV_New_f();
- return;
- }
-
- sv_client.state= cs_spawned;
-
- // call the game begin function
- PlayerClient.ClientBegin(sv_player);
-
- Cbuf.InsertFromDefer();
- }
-
- //=============================================================================
-
- /*
- ==================
- SV_NextDownload_f
- ==================
- */
- public static void SV_NextDownload_f()
- {
- int r;
- int percent;
- int size;
-
- if (sv_client.download == null)
- return;
-
- r= sv_client.downloadsize - sv_client.downloadcount;
- if (r > 1024)
- r= 1024;
-
- MSG.WriteByte(sv_client.netchan.message, svc_download);
- MSG.WriteShort(sv_client.netchan.message, r);
-
- sv_client.downloadcount += r;
- size= sv_client.downloadsize;
- if (size == 0)
- size= 1;
- percent= sv_client.downloadcount * 100 / size;
- MSG.WriteByte(sv_client.netchan.message, percent);
- SZ.Write(sv_client.netchan.message, sv_client.download, sv_client.downloadcount - r, r);
-
- if (sv_client.downloadcount != sv_client.downloadsize)
- return;
-
- FS.FreeFile(sv_client.download);
- sv_client.download= null;
- }
-
- /*
- ==================
- SV_BeginDownload_f
- ==================
- */
- public static void SV_BeginDownload_f()
- {
- String name;
- int offset= 0;
-
- name= Cmd.Argv(1);
-
- if (Cmd.Argc() > 2)
- offset= atoi(Cmd.Argv(2)); // downloaded offset
-
- // hacked by zoid to allow more conrol over download
- // first off, no .. or global allow check
-
- if (name.indexOf("..") != -1
- || allow_download.value == 0 // leading dot is no good
- || name.charAt(0) == '.' // leading slash bad as well, must be in subdir
- || name.charAt(0) == '/' // next up, skin check
- || (name.startsWith("players/") && 0 == allow_download_players.value) // now models
- || (name.startsWith("models/") && 0 == allow_download_models.value) // now sounds
- || (name.startsWith("sound/")
- && 0 == allow_download_sounds.value) // now maps (note special case for maps, must not be in pak)
- || (name.startsWith("maps/") && 0 == allow_download_maps.value) // MUST be in a subdirectory
- || name.indexOf('/') == -1)
- { // don't allow anything with .. path
- MSG.WriteByte(sv_client.netchan.message, svc_download);
- MSG.WriteShort(sv_client.netchan.message, -1);
- MSG.WriteByte(sv_client.netchan.message, 0);
- return;
- }
-
- if (sv_client.download != null)
- FS.FreeFile(sv_client.download);
-
- sv_client.download= FS.LoadFile(name);
- sv_client.downloadsize= sv_client.download.length;
- sv_client.downloadcount= offset;
-
- if (offset > sv_client.downloadsize)
- sv_client.downloadcount= sv_client.downloadsize;
-
- if (sv_client.download == null // special check for maps, if it came from a pak file, don't allow
- // download ZOID
- || (name.startsWith("maps/") && FS.file_from_pak != 0))
- {
- Com.DPrintf("Couldn't download " + name + " to " + sv_client.name + "\n");
- if (sv_client.download != null)
- {
- FS.FreeFile(sv_client.download);
- sv_client.download= null;
- }
-
- MSG.WriteByte(sv_client.netchan.message, svc_download);
- MSG.WriteShort(sv_client.netchan.message, -1);
- MSG.WriteByte(sv_client.netchan.message, 0);
- return;
- }
-
- SV_NextDownload_f();
- Com.DPrintf("Downloading " + name + " to " + sv_client.name + "\n");
- }
-
- //============================================================================
-
- /*
- =================
- SV_Disconnect_f
-
- The client is going to disconnect, so remove the connection immediately
- =================
- */
- public static void SV_Disconnect_f()
- {
- // SV_EndRedirect ();
- SV_DropClient(sv_client);
- }
-
- /*
- ==================
- SV_ShowServerinfo_f
-
- Dumps the serverinfo info string
- ==================
- */
- public static void SV_ShowServerinfo_f()
- {
- Info.Print(Cvar.Serverinfo());
- }
-
- public static void SV_Nextserver()
- {
- String v;
-
- //ZOID, ss_pic can be nextserver'd in coop mode
- if (sv.state == ss_game || (sv.state == ss_pic && 0 == Cvar.VariableValue("coop")))
- return; // can't nextserver while playing a normal game
-
- svs.spawncount++; // make sure another doesn't sneak in
- v= Cvar.VariableString("nextserver");
- //if (!v[0])
- if (v.length() == 0)
- Cbuf.AddText("killserver\n");
- else
- {
- Cbuf.AddText(v);
- Cbuf.AddText("\n");
- }
- Cvar.Set("nextserver", "");
- }
-
- /*
- ==================
- SV_Nextserver_f
-
- A cinematic has completed or been aborted by a client, so move
- to the next server,
- ==================
- */
- public static void SV_Nextserver_f()
- {
- if (Lib.atoi(Cmd.Argv(1)) != svs.spawncount)
- {
- Com.DPrintf("Nextserver() from wrong level, from " + sv_client.name + "\n");
- return; // leftover from last server
- }
-
- Com.DPrintf("Nextserver() from " + sv_client.name + "\n");
-
- SV_Nextserver();
- }
-
- public static class ucmd_t
- {
- public ucmd_t(String n, Runnable r)
- {
- name= n;
- this.r= r;
- }
- String name;
- Runnable r;
- }
-
- static ucmd_t u1= new ucmd_t("new", new Runnable()
- {
- public void run()
- {
- SV_New_f();
- }
- });
-
- static ucmd_t ucmds[]= {
- // auto issued
- new ucmd_t("new", new Runnable()
- { public void run()
- { SV_New_f();
- }
- }), new ucmd_t("configstrings", new Runnable()
- {
- public void run()
- {
- SV_Configstrings_f();
- }
- }), new ucmd_t("baselines", new Runnable()
- {
- public void run()
- {
- SV_Baselines_f();
- }
- }), new ucmd_t("begin", new Runnable()
- {
- public void run()
- {
- SV_Begin_f();
- }
- }), new ucmd_t("nextserver", new Runnable()
- {
- public void run()
- {
- SV_Nextserver_f();
- }
- }), new ucmd_t("disconnect", new Runnable()
- {
- public void run()
- {
- SV_Disconnect_f();
- }
- }),
-
- // issued by hand at client consoles
- new ucmd_t("info", new Runnable()
- {
- public void run()
- {
- SV_ShowServerinfo_f();
- }
- }), new ucmd_t("download", new Runnable()
- {
- public void run()
- {
- SV_BeginDownload_f();
- }
- }), new ucmd_t("nextdl", new Runnable()
- {
- public void run()
- {
- SV_NextDownload_f();
- }
- })
- };
-
- /*
- ==================
- SV_ExecuteUserCommand
- ==================
- */
- public static void SV_ExecuteUserCommand(String s)
- {
- ucmd_t u= null;
-
- Cmd.TokenizeString(s.toCharArray(), true);
- sv_player= sv_client.edict;
-
- // SV_BeginRedirect (RD_CLIENT);
-
- int i= 0;
- for (; i < ucmds.length; i++)
- {
- u= ucmds[i];
- if (0 == strcmp(Cmd.Argv(0), u.name))
- {
- u.r.run();
- break;
- }
- }
-
- if (i == ucmds.length && sv.state == ss_game)
- PlayerClient.ClientCommand(sv_player);
-
- // SV_EndRedirect ();
- }
-
- /*
- ===========================================================================
-
- USER CMD EXECUTION
-
- ===========================================================================
- */
-
- public static void SV_ClientThink(client_t cl, usercmd_t cmd)
- {
- cl.commandMsec -= cmd.msec & 0xFF;
-
- if (cl.commandMsec < 0 && sv_enforcetime.value != 0)
- {
- Com.DPrintf("commandMsec underflow from " + cl.name + "\n");
- return;
- }
-
- PlayerClient.ClientThink(cl.edict, cmd);
- }
-
- public static final int MAX_STRINGCMDS= 8;
- /*
- ===================
- SV_ExecuteClientMessage
-
- The current net_message is parsed for the given client
- ===================
- */
- public static void SV_ExecuteClientMessage(client_t cl)
- {
- int c;
- String s;
-
- usercmd_t nullcmd= new usercmd_t();
- usercmd_t oldest= new usercmd_t(), oldcmd= new usercmd_t(), newcmd= new usercmd_t();
- int net_drop;
- int stringCmdCount;
- int checksum, calculatedChecksum;
- int checksumIndex;
- boolean move_issued;
- int lastframe;
-
- sv_client= cl;
- sv_player= sv_client.edict;
-
- // only allow one move command
- move_issued= false;
- stringCmdCount= 0;
-
- while (true)
- {
- if (net_message.readcount > net_message.cursize)
- {
- Com.Printf("SV_ReadClientMessage: bad read:\n");
- Com.Printf(Lib.hexDump(net_message.data, 32, false));
- SV_DropClient(cl);
- return;
- }
-
- c= MSG.ReadByte(net_message);
- if (c == -1)
- break;
-
- switch (c)
- {
- default :
- Com.Printf("SV_ReadClientMessage: unknown command char\n");
- SV_DropClient(cl);
- return;
-
- case clc_nop :
- break;
-
- case clc_userinfo :
- cl.userinfo= MSG.ReadString(net_message);
- SV_MAIN.SV_UserinfoChanged(cl);
- break;
-
- case clc_move :
- if (move_issued)
- return; // someone is trying to cheat...
-
- move_issued= true;
- checksumIndex= net_message.readcount;
- checksum= MSG.ReadByte(net_message);
- lastframe= MSG.ReadLong(net_message);
-
- if (lastframe != cl.lastframe)
- {
- cl.lastframe= lastframe;
- if (cl.lastframe > 0)
- {
- cl.frame_latency[cl.lastframe & (LATENCY_COUNTS - 1)]=
- svs.realtime - cl.frames[cl.lastframe & UPDATE_MASK].senttime;
- }
- }
-
- //memset (nullcmd, 0, sizeof(nullcmd));
- nullcmd= new usercmd_t();
- MSG.ReadDeltaUsercmd(net_message, nullcmd, oldest);
- MSG.ReadDeltaUsercmd(net_message, oldest, oldcmd);
- MSG.ReadDeltaUsercmd(net_message, oldcmd, newcmd);
-
- if (cl.state != cs_spawned)
- {
- cl.lastframe= -1;
- break;
- }
-
- // if the checksum fails, ignore the rest of the packet
-
- calculatedChecksum=
- Com.BlockSequenceCRCByte(
- net_message.data, checksumIndex + 1,
- net_message.readcount - checksumIndex - 1,
- cl.netchan.incoming_sequence);
-
- if ((calculatedChecksum &0xff) != checksum)
- {
- Com.DPrintf(
- "Failed command checksum for "
- + cl.name
- + " ("
- + calculatedChecksum
- + " != "
- + checksum
- + ")/"
- + cl.netchan.incoming_sequence
- + "\n");
- return;
- }
-
- if (0 == sv_paused.value)
- {
- net_drop= cl.netchan.dropped;
- if (net_drop < 20)
- {
-
- //if (net_drop > 2)
-
- // Com.Printf ("drop %i\n", net_drop);
- while (net_drop > 2)
- {
- SV_ClientThink(cl, cl.lastcmd);
-
- net_drop--;
- }
- if (net_drop > 1)
- SV_ClientThink(cl, oldest);
-
- if (net_drop > 0)
- SV_ClientThink(cl, oldcmd);
-
- }
- SV_ClientThink(cl, newcmd);
- }
-
- // copy.
- cl.lastcmd.set(newcmd);
- break;
-
- case clc_stringcmd :
- s= MSG.ReadString(net_message);
-
- // malicious users may try using too many string commands
- if (++stringCmdCount < MAX_STRINGCMDS)
- SV_ExecuteUserCommand(s);
-
- if (cl.state == cs_zombie)
- return; // disconnect command
- break;
- }
- }
- }
-}
+public class SV_USER {
+
+ static edict_t sv_player;
+
+ public static class ucmd_t {
+ public ucmd_t(String n, Runnable r) {
+ name = n;
+ this.r = r;
+ }
+
+ String name;
+
+ Runnable r;
+ }
+
+ static ucmd_t u1 = new ucmd_t("new", new Runnable() {
+ public void run() {
+ SV_USER.SV_New_f();
+ }
+ });
+
+ static ucmd_t ucmds[] = {
+ // auto issued
+ new ucmd_t("new", new Runnable() {
+ public void run() {
+ SV_USER.SV_New_f();
+ }
+ }), new ucmd_t("configstrings", new Runnable() {
+ public void run() {
+ SV_USER.SV_Configstrings_f();
+ }
+ }), new ucmd_t("baselines", new Runnable() {
+ public void run() {
+ SV_USER.SV_Baselines_f();
+ }
+ }), new ucmd_t("begin", new Runnable() {
+ public void run() {
+ SV_USER.SV_Begin_f();
+ }
+ }), new ucmd_t("nextserver", new Runnable() {
+ public void run() {
+ SV_USER.SV_Nextserver_f();
+ }
+ }), new ucmd_t("disconnect", new Runnable() {
+ public void run() {
+ SV_USER.SV_Disconnect_f();
+ }
+ }),
+
+ // issued by hand at client consoles
+ new ucmd_t("info", new Runnable() {
+ public void run() {
+ SV_USER.SV_ShowServerinfo_f();
+ }
+ }), new ucmd_t("download", new Runnable() {
+ public void run() {
+ SV_USER.SV_BeginDownload_f();
+ }
+ }), new ucmd_t("nextdl", new Runnable() {
+ public void run() {
+ SV_USER.SV_NextDownload_f();
+ }
+ }) };
+
+ public static final int MAX_STRINGCMDS = 8;
+
+ /*
+ * ============================================================
+ *
+ * USER STRINGCMD EXECUTION
+ *
+ * sv_client and sv_player will be valid.
+ * ============================================================
+ */
+
+ /*
+ * ================== SV_BeginDemoServer ==================
+ */
+ public static void SV_BeginDemoserver() {
+ String name;
+
+ name = "demos/" + SV_INIT.sv.name;
+ try {
+ SV_INIT.sv.demofile = FS.FOpenFile(name);
+ } catch (IOException e) {
+ Com.Error(Defines.ERR_DROP, "Couldn't open " + name + "\n");
+ }
+ if (SV_INIT.sv.demofile == null)
+ Com.Error(Defines.ERR_DROP, "Couldn't open " + name + "\n");
+ }
+
+ /*
+ * ================ SV_New_f
+ *
+ * Sends the first message from the server to a connected client. This will
+ * be sent on the initial connection and upon each server load.
+ * ================
+ */
+ public static void SV_New_f() {
+ String gamedir;
+ int playernum;
+ edict_t ent;
+
+ Com.DPrintf("New() from " + SV_MAIN.sv_client.name + "\n");
+
+ if (SV_MAIN.sv_client.state != Defines.cs_connected) {
+ Com.Printf("New not valid -- already spawned\n");
+ return;
+ }
+
+ // demo servers just dump the file message
+ if (SV_INIT.sv.state == Defines.ss_demo) {
+ SV_BeginDemoserver();
+ return;
+ }
+
+ //
+ // serverdata needs to go over for all types of servers
+ // to make sure the protocol is right, and to set the gamedir
+ //
+ gamedir = Cvar.VariableString("gamedir");
+
+ // send the serverdata
+ MSG
+ .WriteByte(SV_MAIN.sv_client.netchan.message,
+ Defines.svc_serverdata);
+ MSG.WriteInt(SV_MAIN.sv_client.netchan.message,
+ Defines.PROTOCOL_VERSION);
+ MSG
+ .WriteLong(SV_MAIN.sv_client.netchan.message,
+ SV_INIT.svs.spawncount);
+ MSG.WriteByte(SV_MAIN.sv_client.netchan.message,
+ SV_INIT.sv.attractloop ? 1 : 0);
+ MSG.WriteString(SV_MAIN.sv_client.netchan.message, gamedir);
+
+ if (SV_INIT.sv.state == Defines.ss_cinematic
+ || SV_INIT.sv.state == Defines.ss_pic)
+ playernum = -1;
+ else
+ //playernum = sv_client - svs.clients;
+ playernum = SV_MAIN.sv_client.serverindex;
+
+ MSG.WriteShort(SV_MAIN.sv_client.netchan.message, playernum);
+
+ // send full levelname
+ MSG.WriteString(SV_MAIN.sv_client.netchan.message,
+ SV_INIT.sv.configstrings[Defines.CS_NAME]);
+
+ //
+ // game server
+ //
+ if (SV_INIT.sv.state == Defines.ss_game) {
+ // set up the entity for the client
+ ent = GameBase.g_edicts[playernum + 1];
+ ent.s.number = playernum + 1;
+ SV_MAIN.sv_client.edict = ent;
+ SV_MAIN.sv_client.lastcmd = new usercmd_t();
+
+ // begin fetching configstrings
+ MSG.WriteByte(SV_MAIN.sv_client.netchan.message,
+ Defines.svc_stufftext);
+ MSG.WriteString(SV_MAIN.sv_client.netchan.message,
+ "cmd configstrings " + SV_INIT.svs.spawncount + " 0\n");
+ }
+
+ }
+
+ /*
+ * ================== SV_Configstrings_f ==================
+ */
+ public static void SV_Configstrings_f() {
+ int start;
+
+ Com.DPrintf("Configstrings() from " + SV_MAIN.sv_client.name + "\n");
+
+ if (SV_MAIN.sv_client.state != Defines.cs_connected) {
+ Com.Printf("configstrings not valid -- already spawned\n");
+ return;
+ }
+
+ // handle the case of a level changing while a client was connecting
+ if (Lib.atoi(Cmd.Argv(1)) != SV_INIT.svs.spawncount) {
+ Com.Printf("SV_Configstrings_f from different level\n");
+ SV_New_f();
+ return;
+ }
+
+ start = Lib.atoi(Cmd.Argv(2));
+
+ // write a packet full of data
+
+ while (SV_MAIN.sv_client.netchan.message.cursize < Defines.MAX_MSGLEN / 2
+ && start < Defines.MAX_CONFIGSTRINGS) {
+ if (SV_INIT.sv.configstrings[start] != null
+ && SV_INIT.sv.configstrings[start].length() != 0) {
+ MSG.WriteByte(SV_MAIN.sv_client.netchan.message,
+ Defines.svc_configstring);
+ MSG.WriteShort(SV_MAIN.sv_client.netchan.message, start);
+ MSG.WriteString(SV_MAIN.sv_client.netchan.message,
+ SV_INIT.sv.configstrings[start]);
+ }
+ start++;
+ }
+
+ // send next command
+
+ if (start == Defines.MAX_CONFIGSTRINGS) {
+ MSG.WriteByte(SV_MAIN.sv_client.netchan.message,
+ Defines.svc_stufftext);
+ MSG.WriteString(SV_MAIN.sv_client.netchan.message, "cmd baselines "
+ + SV_INIT.svs.spawncount + " 0\n");
+ } else {
+ MSG.WriteByte(SV_MAIN.sv_client.netchan.message,
+ Defines.svc_stufftext);
+ MSG.WriteString(SV_MAIN.sv_client.netchan.message,
+ "cmd configstrings " + SV_INIT.svs.spawncount + " " + start
+ + "\n");
+ }
+ }
+
+ /*
+ * ================== SV_Baselines_f ==================
+ */
+ public static void SV_Baselines_f() {
+ int start;
+ entity_state_t nullstate;
+ entity_state_t base;
+
+ Com.DPrintf("Baselines() from " + SV_MAIN.sv_client.name + "\n");
+
+ if (SV_MAIN.sv_client.state != Defines.cs_connected) {
+ Com.Printf("baselines not valid -- already spawned\n");
+ return;
+ }
+
+ // handle the case of a level changing while a client was connecting
+ if (Lib.atoi(Cmd.Argv(1)) != SV_INIT.svs.spawncount) {
+ Com.Printf("SV_Baselines_f from different level\n");
+ SV_New_f();
+ return;
+ }
+
+ start = Lib.atoi(Cmd.Argv(2));
+
+ //memset (&nullstate, 0, sizeof(nullstate));
+ nullstate = new entity_state_t(null);
+
+ // write a packet full of data
+
+ while (SV_MAIN.sv_client.netchan.message.cursize < Defines.MAX_MSGLEN / 2
+ && start < Defines.MAX_EDICTS) {
+ base = SV_INIT.sv.baselines[start];
+ if (base.modelindex != 0 || base.sound != 0 || base.effects != 0) {
+ MSG.WriteByte(SV_MAIN.sv_client.netchan.message,
+ Defines.svc_spawnbaseline);
+ MSG.WriteDeltaEntity(nullstate, base,
+ SV_MAIN.sv_client.netchan.message, true, true);
+ }
+ start++;
+ }
+
+ // send next command
+
+ if (start == Defines.MAX_EDICTS) {
+ MSG.WriteByte(SV_MAIN.sv_client.netchan.message,
+ Defines.svc_stufftext);
+ MSG.WriteString(SV_MAIN.sv_client.netchan.message, "precache "
+ + SV_INIT.svs.spawncount + "\n");
+ } else {
+ MSG.WriteByte(SV_MAIN.sv_client.netchan.message,
+ Defines.svc_stufftext);
+ MSG.WriteString(SV_MAIN.sv_client.netchan.message, "cmd baselines "
+ + SV_INIT.svs.spawncount + " " + start + "\n");
+ }
+ }
+
+ /*
+ * ================== SV_Begin_f ==================
+ */
+ public static void SV_Begin_f() {
+ Com.DPrintf("Begin() from " + SV_MAIN.sv_client.name + "\n");
+
+ // handle the case of a level changing while a client was connecting
+ if (Lib.atoi(Cmd.Argv(1)) != SV_INIT.svs.spawncount) {
+ Com.Printf("SV_Begin_f from different level\n");
+ SV_New_f();
+ return;
+ }
+
+ SV_MAIN.sv_client.state = Defines.cs_spawned;
+
+ // call the game begin function
+ PlayerClient.ClientBegin(SV_USER.sv_player);
+
+ Cbuf.InsertFromDefer();
+ }
+
+ //=============================================================================
+
+ /*
+ * ================== SV_NextDownload_f ==================
+ */
+ public static void SV_NextDownload_f() {
+ int r;
+ int percent;
+ int size;
+
+ if (SV_MAIN.sv_client.download == null)
+ return;
+
+ r = SV_MAIN.sv_client.downloadsize - SV_MAIN.sv_client.downloadcount;
+ if (r > 1024)
+ r = 1024;
+
+ MSG.WriteByte(SV_MAIN.sv_client.netchan.message, Defines.svc_download);
+ MSG.WriteShort(SV_MAIN.sv_client.netchan.message, r);
+
+ SV_MAIN.sv_client.downloadcount += r;
+ size = SV_MAIN.sv_client.downloadsize;
+ if (size == 0)
+ size = 1;
+ percent = SV_MAIN.sv_client.downloadcount * 100 / size;
+ MSG.WriteByte(SV_MAIN.sv_client.netchan.message, percent);
+ SZ.Write(SV_MAIN.sv_client.netchan.message, SV_MAIN.sv_client.download,
+ SV_MAIN.sv_client.downloadcount - r, r);
+
+ if (SV_MAIN.sv_client.downloadcount != SV_MAIN.sv_client.downloadsize)
+ return;
+
+ FS.FreeFile(SV_MAIN.sv_client.download);
+ SV_MAIN.sv_client.download = null;
+ }
+
+ /*
+ * ================== SV_BeginDownload_f ==================
+ */
+ public static void SV_BeginDownload_f() {
+ String name;
+ int offset = 0;
+
+ name = Cmd.Argv(1);
+
+ if (Cmd.Argc() > 2)
+ offset = Lib.atoi(Cmd.Argv(2)); // downloaded offset
+
+ // hacked by zoid to allow more conrol over download
+ // first off, no .. or global allow check
+
+ if (name.indexOf("..") != -1
+ || SV_MAIN.allow_download.value == 0 // leading dot is no good
+ || name.charAt(0) == '.' // leading slash bad as well, must be
+ // in subdir
+ || name.charAt(0) == '/' // next up, skin check
+ || (name.startsWith("players/") && 0 == SV_MAIN.allow_download_players.value) // now
+ // models
+ || (name.startsWith("models/") && 0 == SV_MAIN.allow_download_models.value) // now
+ // sounds
+ || (name.startsWith("sound/") && 0 == SV_MAIN.allow_download_sounds.value)
+ // now maps (note special case for maps, must not be in pak)
+ || (name.startsWith("maps/") && 0 == SV_MAIN.allow_download_maps.value) // MUST
+ // be
+ // in a
+ // subdirectory
+ || name.indexOf('/') == -1) { // don't allow anything with ..
+ // path
+ MSG.WriteByte(SV_MAIN.sv_client.netchan.message,
+ Defines.svc_download);
+ MSG.WriteShort(SV_MAIN.sv_client.netchan.message, -1);
+ MSG.WriteByte(SV_MAIN.sv_client.netchan.message, 0);
+ return;
+ }
+
+ if (SV_MAIN.sv_client.download != null)
+ FS.FreeFile(SV_MAIN.sv_client.download);
+
+ SV_MAIN.sv_client.download = FS.LoadFile(name);
+ SV_MAIN.sv_client.downloadsize = SV_MAIN.sv_client.download.length;
+ SV_MAIN.sv_client.downloadcount = offset;
+
+ if (offset > SV_MAIN.sv_client.downloadsize)
+ SV_MAIN.sv_client.downloadcount = SV_MAIN.sv_client.downloadsize;
+
+ if (SV_MAIN.sv_client.download == null // special check for maps, if it
+ // came from a pak file, don't
+ // allow
+ // download ZOID
+ || (name.startsWith("maps/") && FS.file_from_pak != 0)) {
+ Com.DPrintf("Couldn't download " + name + " to "
+ + SV_MAIN.sv_client.name + "\n");
+ if (SV_MAIN.sv_client.download != null) {
+ FS.FreeFile(SV_MAIN.sv_client.download);
+ SV_MAIN.sv_client.download = null;
+ }
+
+ MSG.WriteByte(SV_MAIN.sv_client.netchan.message,
+ Defines.svc_download);
+ MSG.WriteShort(SV_MAIN.sv_client.netchan.message, -1);
+ MSG.WriteByte(SV_MAIN.sv_client.netchan.message, 0);
+ return;
+ }
+
+ SV_NextDownload_f();
+ Com.DPrintf("Downloading " + name + " to " + SV_MAIN.sv_client.name
+ + "\n");
+ }
+
+ //============================================================================
+
+ /*
+ * ================= SV_Disconnect_f
+ *
+ * The client is going to disconnect, so remove the connection immediately
+ * =================
+ */
+ public static void SV_Disconnect_f() {
+ // SV_EndRedirect ();
+ SV_MAIN.SV_DropClient(SV_MAIN.sv_client);
+ }
+
+ /*
+ * ================== SV_ShowServerinfo_f
+ *
+ * Dumps the serverinfo info string ==================
+ */
+ public static void SV_ShowServerinfo_f() {
+ Info.Print(Cvar.Serverinfo());
+ }
+
+ public static void SV_Nextserver() {
+ String v;
+
+ //ZOID, ss_pic can be nextserver'd in coop mode
+ if (SV_INIT.sv.state == Defines.ss_game
+ || (SV_INIT.sv.state == Defines.ss_pic && 0 == Cvar
+ .VariableValue("coop")))
+ return; // can't nextserver while playing a normal game
+
+ SV_INIT.svs.spawncount++; // make sure another doesn't sneak in
+ v = Cvar.VariableString("nextserver");
+ //if (!v[0])
+ if (v.length() == 0)
+ Cbuf.AddText("killserver\n");
+ else {
+ Cbuf.AddText(v);
+ Cbuf.AddText("\n");
+ }
+ Cvar.Set("nextserver", "");
+ }
+
+ /*
+ * ================== SV_Nextserver_f
+ *
+ * A cinematic has completed or been aborted by a client, so move to the
+ * next server, ==================
+ */
+ public static void SV_Nextserver_f() {
+ if (Lib.atoi(Cmd.Argv(1)) != SV_INIT.svs.spawncount) {
+ Com.DPrintf("Nextserver() from wrong level, from "
+ + SV_MAIN.sv_client.name + "\n");
+ return; // leftover from last server
+ }
+
+ Com.DPrintf("Nextserver() from " + SV_MAIN.sv_client.name + "\n");
+
+ SV_Nextserver();
+ }
+
+ /*
+ * ================== SV_ExecuteUserCommand ==================
+ */
+ public static void SV_ExecuteUserCommand(String s) {
+ SV_USER.ucmd_t u = null;
+
+ Cmd.TokenizeString(s.toCharArray(), true);
+ SV_USER.sv_player = SV_MAIN.sv_client.edict;
+
+ // SV_BeginRedirect (RD_CLIENT);
+
+ int i = 0;
+ for (; i < SV_USER.ucmds.length; i++) {
+ u = SV_USER.ucmds[i];
+ if (0 == Lib.strcmp(Cmd.Argv(0), u.name)) {
+ u.r.run();
+ break;
+ }
+ }
+
+ if (i == SV_USER.ucmds.length && SV_INIT.sv.state == Defines.ss_game)
+ GameAI.ClientCommand(SV_USER.sv_player);
+
+ // SV_EndRedirect ();
+ }
+
+ /*
+ * ===========================================================================
+ *
+ * USER CMD EXECUTION
+ *
+ * ===========================================================================
+ */
+
+ public static void SV_ClientThink(client_t cl, usercmd_t cmd) {
+ cl.commandMsec -= cmd.msec & 0xFF;
+
+ if (cl.commandMsec < 0 && SV_MAIN.sv_enforcetime.value != 0) {
+ Com.DPrintf("commandMsec underflow from " + cl.name + "\n");
+ return;
+ }
+
+ PlayerClient.ClientThink(cl.edict, cmd);
+ }
+
+ /*
+ * =================== SV_ExecuteClientMessage
+ *
+ * The current net_message is parsed for the given client
+ * ===================
+ */
+ public static void SV_ExecuteClientMessage(client_t cl) {
+ int c;
+ String s;
+
+ usercmd_t nullcmd = new usercmd_t();
+ usercmd_t oldest = new usercmd_t(), oldcmd = new usercmd_t(), newcmd = new usercmd_t();
+ int net_drop;
+ int stringCmdCount;
+ int checksum, calculatedChecksum;
+ int checksumIndex;
+ boolean move_issued;
+ int lastframe;
+
+ SV_MAIN.sv_client = cl;
+ SV_USER.sv_player = SV_MAIN.sv_client.edict;
+
+ // only allow one move command
+ move_issued = false;
+ stringCmdCount = 0;
+
+ while (true) {
+ if (Globals.net_message.readcount > Globals.net_message.cursize) {
+ Com.Printf("SV_ReadClientMessage: bad read:\n");
+ Com.Printf(Lib.hexDump(Globals.net_message.data, 32, false));
+ SV_MAIN.SV_DropClient(cl);
+ return;
+ }
+
+ c = MSG.ReadByte(Globals.net_message);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ default:
+ Com.Printf("SV_ReadClientMessage: unknown command char\n");
+ SV_MAIN.SV_DropClient(cl);
+ return;
+
+ case Defines.clc_nop:
+ break;
+
+ case Defines.clc_userinfo:
+ cl.userinfo = MSG.ReadString(Globals.net_message);
+ SV_MAIN.SV_UserinfoChanged(cl);
+ break;
+
+ case Defines.clc_move:
+ if (move_issued)
+ return; // someone is trying to cheat...
+
+ move_issued = true;
+ checksumIndex = Globals.net_message.readcount;
+ checksum = MSG.ReadByte(Globals.net_message);
+ lastframe = MSG.ReadLong(Globals.net_message);
+
+ if (lastframe != cl.lastframe) {
+ cl.lastframe = lastframe;
+ if (cl.lastframe > 0) {
+ cl.frame_latency[cl.lastframe
+ & (Defines.LATENCY_COUNTS - 1)] = SV_INIT.svs.realtime
+ - cl.frames[cl.lastframe & Defines.UPDATE_MASK].senttime;
+ }
+ }
+
+ //memset (nullcmd, 0, sizeof(nullcmd));
+ nullcmd = new usercmd_t();
+ MSG.ReadDeltaUsercmd(Globals.net_message, nullcmd, oldest);
+ MSG.ReadDeltaUsercmd(Globals.net_message, oldest, oldcmd);
+ MSG.ReadDeltaUsercmd(Globals.net_message, oldcmd, newcmd);
+
+ if (cl.state != Defines.cs_spawned) {
+ cl.lastframe = -1;
+ break;
+ }
+
+ // if the checksum fails, ignore the rest of the packet
+
+ calculatedChecksum = Com.BlockSequenceCRCByte(
+ Globals.net_message.data, checksumIndex + 1,
+ Globals.net_message.readcount - checksumIndex - 1,
+ cl.netchan.incoming_sequence);
+
+ if ((calculatedChecksum & 0xff) != checksum) {
+ Com.DPrintf("Failed command checksum for " + cl.name + " ("
+ + calculatedChecksum + " != " + checksum + ")/"
+ + cl.netchan.incoming_sequence + "\n");
+ return;
+ }
+
+ if (0 == SV_MAIN.sv_paused.value) {
+ net_drop = cl.netchan.dropped;
+ if (net_drop < 20) {
+
+ //if (net_drop > 2)
+
+ // Com.Printf ("drop %i\n", net_drop);
+ while (net_drop > 2) {
+ SV_ClientThink(cl, cl.lastcmd);
+
+ net_drop--;
+ }
+ if (net_drop > 1)
+ SV_ClientThink(cl, oldest);
+
+ if (net_drop > 0)
+ SV_ClientThink(cl, oldcmd);
+
+ }
+ SV_ClientThink(cl, newcmd);
+ }
+
+ // copy.
+ cl.lastcmd.set(newcmd);
+ break;
+
+ case Defines.clc_stringcmd:
+ s = MSG.ReadString(Globals.net_message);
+
+ // malicious users may try using too many string commands
+ if (++stringCmdCount < SV_USER.MAX_STRINGCMDS)
+ SV_ExecuteUserCommand(s);
+
+ if (cl.state == Defines.cs_zombie)
+ return; // disconnect command
+ break;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jake2/server/SV_WORLD.java b/src/jake2/server/SV_WORLD.java
index 46caf89..1d5fbb9 100644
--- a/src/jake2/server/SV_WORLD.java
+++ b/src/jake2/server/SV_WORLD.java
@@ -1,682 +1,528 @@
/*
-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 07.01.2000 by RST.
-// $Id: SV_WORLD.java,v 1.6 2004-08-29 21:39:25 hzi Exp $
-
+// $Id: SV_WORLD.java,v 1.7 2004-09-22 19:22:12 salomo Exp $
package jake2.server;
-import jake2.game.*;
+import jake2.Defines;
+import jake2.Globals;
+import jake2.game.GameBase;
+import jake2.game.cmodel_t;
+import jake2.game.edict_t;
+import jake2.game.link_t;
+import jake2.game.trace_t;
import jake2.qcommon.CM;
import jake2.qcommon.Com;
-
-public class SV_WORLD extends SV_CCMDS
-{
-
- // world.c -- world query functions
-
- //
- //
- //===============================================================================
- //
- //ENTITY AREA CHECKING
- //
- //FIXME: this use of "area" is different from the bsp file use
- //===============================================================================
- //
-
- public static void initNodes()
- {
- for (int n= 0; n < AREA_NODES; n++)
- sv_areanodes[n]= new areanode_t();
- }
-
- public static areanode_t sv_areanodes[]= new areanode_t[AREA_NODES];
-
- static {
- initNodes();
- }
- public static int sv_numareanodes;
-
- public static float area_mins[], area_maxs[];
- public static edict_t area_list[];
- public static int area_count, area_maxcount;
- public static int area_type;
-
- // ClearLink is used for new headnodes
- public static void ClearLink(link_t l)
- {
- l.prev= l.next= l;
- }
-
- public static void RemoveLink(link_t l)
- {
- l.next.prev= l.prev;
- l.prev.next= l.next;
- }
-
- public static void InsertLinkBefore(link_t l, link_t before)
- {
- l.next= before;
- l.prev= before.prev;
- l.prev.next= l;
- l.next.prev= l;
- }
-
- /*
- ===============
- SV_CreateAreaNode
-
- Builds a uniformly subdivided tree for the given world size
- ===============
- */
- public static areanode_t SV_CreateAreaNode(int depth, float[] mins, float[] maxs)
- {
- areanode_t anode;
- float[] size= { 0, 0, 0 };
- float[] mins1= { 0, 0, 0 }, maxs1= { 0, 0, 0 }, mins2= { 0, 0, 0 }, maxs2= { 0, 0, 0 };
-
- anode= sv_areanodes[sv_numareanodes];
-
- // just for debugging (rst)
- VectorCopy(mins, anode.mins_rst);
- VectorCopy(maxs, anode.maxs_rst);
-
- sv_numareanodes++;
-
- ClearLink(anode.trigger_edicts);
- ClearLink(anode.solid_edicts);
-
- if (depth == AREA_DEPTH)
- {
- anode.axis= -1;
- anode.children[0]= anode.children[1]= null;
- return anode;
- }
-
- VectorSubtract(maxs, mins, size);
- if (size[0] > size[1])
- anode.axis= 0;
- else
- anode.axis= 1;
-
- anode.dist= 0.5f * (maxs[anode.axis] + mins[anode.axis]);
- VectorCopy(mins, mins1);
- VectorCopy(mins, mins2);
- VectorCopy(maxs, maxs1);
- VectorCopy(maxs, maxs2);
-
- maxs1[anode.axis]= mins2[anode.axis]= anode.dist;
-
- anode.children[0]= SV_CreateAreaNode(depth + 1, mins2, maxs2);
- anode.children[1]= SV_CreateAreaNode(depth + 1, mins1, maxs1);
-
- return anode;
- }
-
- /*
- ===============
- SV_ClearWorld
-
- ===============
- */
- public static void SV_ClearWorld()
- {
- initNodes();
- sv_numareanodes= 0;
- SV_CreateAreaNode(0, sv.models[1].mins, sv.models[1].maxs);
-
- /*
- Com.p("areanodes:" + sv_numareanodes + " (sollten 32 sein).");
- for (int n = 0; n < sv_numareanodes; n++) {
- Com.Printf(
- "|%3i|%2i|%8.2f |%8.2f|%8.2f|%8.2f| %8.2f|%8.2f|%8.2f|\n",
- new Vargs()
- .add(n)
- .add(sv_areanodes[n].axis)
- .add(sv_areanodes[n].dist)
- .add(sv_areanodes[n].mins_rst[0])
- .add(sv_areanodes[n].mins_rst[1])
- .add(sv_areanodes[n].mins_rst[2])
- .add(sv_areanodes[n].maxs_rst[0])
- .add(sv_areanodes[n].maxs_rst[1])
- .add(sv_areanodes[n].maxs_rst[2]));
- }
- */
- }
- /*
- ===============
- SV_UnlinkEdict
- ===============
- */
- public static void SV_UnlinkEdict(edict_t ent)
- {
- if (null == ent.area.prev)
- return; // not linked in anywhere
-
- RemoveLink(ent.area);
- ent.area.prev= ent.area.next= null;
- }
-
- /*
- ===============
- SV_LinkEdict
- ===============
- */
- public static final int MAX_TOTAL_ENT_LEAFS= 128;
- private static int leafs[]= new int[MAX_TOTAL_ENT_LEAFS];
- private static int clusters[]= new int[MAX_TOTAL_ENT_LEAFS];
-
- public static void SV_LinkEdict(edict_t ent)
- {
- areanode_t node;
-
- int num_leafs;
- int j, k;
- int area;
- int topnode= 0;
-
- if (ent.area.prev != null)
- SV_UnlinkEdict(ent); // unlink from old position
-
- if (ent == GameBase.g_edicts[0])
- return; // don't add the world
-
- if (!ent.inuse)
- return;
-
- // set the size
- VectorSubtract(ent.maxs, ent.mins, ent.size);
-
- // encode the size into the entity_state for client prediction
- if (ent.solid == SOLID_BBOX && 0 == (ent.svflags & SVF_DEADMONSTER))
- {
- // assume that x/y are equal and symetric
- int i= (int) (ent.maxs[0] / 8);
- if (i < 1)
- i= 1;
- if (i > 31)
- i= 31;
-
- // z is not symetric
- j= (int) ((-ent.mins[2]) / 8);
- if (j < 1)
- j= 1;
- if (j > 31)
- j= 31;
-
- // and z maxs can be negative...
- k= (int) ((ent.maxs[2] + 32) / 8);
- if (k < 1)
- k= 1;
- if (k > 63)
- k= 63;
-
- ent.s.solid= (k << 10) | (j << 5) | i;
- }
- else if (ent.solid == SOLID_BSP)
- {
- ent.s.solid= 31; // a solid_bbox will never create this value
- }
- else
- ent.s.solid= 0;
-
- // set the abs box
- if (ent.solid == SOLID_BSP && (ent.s.angles[0] != 0 || ent.s.angles[1] != 0 || ent.s.angles[2] != 0))
- {
- // expand for rotation
- float max, v;
-
- max= 0;
- for (int i= 0; i < 3; i++)
- {
- v= Math.abs(ent.mins[i]);
- if (v > max)
- max= v;
- v= Math.abs(ent.maxs[i]);
- if (v > max)
- max= v;
- }
- for (int i= 0; i < 3; i++)
- {
- ent.absmin[i]= ent.s.origin[i] - max;
- ent.absmax[i]= ent.s.origin[i] + max;
- }
- }
- else
- {
- // normal
- VectorAdd(ent.s.origin, ent.mins, ent.absmin);
- VectorAdd(ent.s.origin, ent.maxs, ent.absmax);
- }
-
- // because movement is clipped an epsilon away from an actual edge,
- // we must fully check even when bounding boxes don't quite touch
-
- ent.absmin[0]--;
- ent.absmin[1]--;
- ent.absmin[2]--;
- ent.absmax[0]++;
- ent.absmax[1]++;
- ent.absmax[2]++;
-
- // link to PVS leafs
- ent.num_clusters= 0;
- ent.areanum= 0;
- ent.areanum2= 0;
-
- // get all leafs, including solids
-
- int iw[] = {topnode};
-
- num_leafs= CM.CM_BoxLeafnums(ent.absmin, ent.absmax, leafs, MAX_TOTAL_ENT_LEAFS, iw);
-
- topnode= iw[0];
-
- // set areas
- for (int i= 0; i < num_leafs; i++)
- {
- clusters[i]= CM.CM_LeafCluster(leafs[i]);
- area= CM.CM_LeafArea(leafs[i]);
- if (area != 0)
- {
- // doors may legally straggle two areas,
- // but nothing should evern need more than that
- if (ent.areanum != 0 && ent.areanum != area)
- {
- if (ent.areanum2 != 0 && ent.areanum2 != area && sv.state == ss_loading)
- Com.DPrintf("Object touching 3 areas at " + ent.absmin[0] + " " + ent.absmin[1] + " " + ent.absmin[2] + "\n");
- ent.areanum2= area;
- }
- else
- ent.areanum= area;
- }
- }
-
- if (num_leafs >= MAX_TOTAL_ENT_LEAFS)
- {
- // assume we missed some leafs, and mark by headnode
- ent.num_clusters= -1;
- ent.headnode= topnode;
- }
- else
- {
- ent.num_clusters= 0;
- for (int i= 0; i < num_leafs; i++)
- {
- if (clusters[i] == -1)
- continue; // not a visible leaf
- for (j= 0; j < i; j++)
- if (clusters[j] == clusters[i])
- break;
- if (j == i)
- {
- if (ent.num_clusters == MAX_ENT_CLUSTERS)
- {
- // assume we missed some leafs, and mark by headnode
- ent.num_clusters= -1;
- ent.headnode= topnode;
- break;
- }
-
- ent.clusternums[ent.num_clusters++]= clusters[i];
- }
- }
- }
-
- // if first time, make sure old_origin is valid
- if (0 == ent.linkcount)
- {
- VectorCopy(ent.s.origin, ent.s.old_origin);
- }
- ent.linkcount++;
-
- if (ent.solid == SOLID_NOT)
- return;
-
- // find the first node that the ent's box crosses
- node= sv_areanodes[0];
-
- while (true)
- {
- if (node.axis == -1)
- break;
- if (ent.absmin[node.axis] > node.dist)
- node= node.children[0];
- else if (ent.absmax[node.axis] < node.dist)
- node= node.children[1];
- else
- break; // crosses the node
- }
-
- // link it in
- if (ent.solid == SOLID_TRIGGER)
- InsertLinkBefore(ent.area, node.trigger_edicts);
- else
- InsertLinkBefore(ent.area, node.solid_edicts);
-
- }
-
- /*
- ====================
- SV_AreaEdicts_r
-
- ====================
- */
- public static void SV_AreaEdicts_r(areanode_t node)
- {
- link_t l, next, start;
- edict_t check;
- int count;
-
- count= 0;
-
- // touch linked edicts
- if (area_type == AREA_SOLID)
- start= node.solid_edicts;
- else
- start= node.trigger_edicts;
-
- for (l= start.next; l != start; l= next)
- {
- next= l.next;
- check= (edict_t) l.o;
-
- if (check.solid == SOLID_NOT)
- continue; // deactivated
- if (check.absmin[0] > area_maxs[0]
- || check.absmin[1] > area_maxs[1]
- || check.absmin[2] > area_maxs[2]
- || check.absmax[0] < area_mins[0]
- || check.absmax[1] < area_mins[1]
- || check.absmax[2] < area_mins[2])
- continue; // not touching
-
- if (area_count == area_maxcount)
- {
- Com.Printf("SV_AreaEdicts: MAXCOUNT\n");
- return;
- }
-
- area_list[area_count]= check;
- area_count++;
- }
-
- if (node.axis == -1)
- return; // terminal node
-
- // recurse down both sides
- if (area_maxs[node.axis] > node.dist)
- SV_AreaEdicts_r(node.children[0]);
-
- if (area_mins[node.axis] < node.dist)
- SV_AreaEdicts_r(node.children[1]);
- }
-
- /*
- ================
- SV_AreaEdicts
- ================
- */
- public static int SV_AreaEdicts(float[] mins, float[] maxs, edict_t list[], int maxcount, int areatype)
- {
- area_mins= mins;
- area_maxs= maxs;
- area_list= list;
- area_count= 0;
- area_maxcount= maxcount;
- area_type= areatype;
-
- SV_AreaEdicts_r(sv_areanodes[0]);
-
- return area_count;
- }
-
- //===========================================================================
-
- private static edict_t touch[]= new edict_t[MAX_EDICTS];
- /*
- =============
- SV_PointContents
- =============
- */
-
- public static int SV_PointContents(float[] p)
- {
- edict_t hit;
-
- int i, num;
- int contents, c2;
- int headnode;
- float angles[];
-
- // get base contents from world
- contents= CM.PointContents(p, sv.models[1].headnode);
-
- // or in contents from all the other entities
- num= SV_AreaEdicts(p, p, touch, MAX_EDICTS, AREA_SOLID);
-
- for (i= 0; i < num; i++)
- {
- hit= touch[i];
-
- // might intersect, so do an exact clip
- headnode= SV_HullForEntity(hit);
- angles= hit.s.angles;
- if (hit.solid != SOLID_BSP)
- angles= vec3_origin; // boxes don't rotate
-
- c2= CM.TransformedPointContents(p, headnode, hit.s.origin, hit.s.angles);
-
- contents |= c2;
- }
-
- return contents;
- }
-
- /*
- ================
- SV_HullForEntity
-
- Returns a headnode that can be used for testing or clipping an
- object of mins/maxs size.
- Offset is filled in to contain the adjustment that must be added to the
- testing object's origin to get a point to use with the returned hull.
- ================
- */
- public static int SV_HullForEntity(edict_t ent)
- {
-
- cmodel_t model;
-
- // decide which clipping hull to use, based on the size
- if (ent.solid == SOLID_BSP)
- { // explicit hulls in the BSP model
- model= sv.models[ent.s.modelindex];
-
- if (null == model)
- Com.Error(ERR_FATAL, "MOVETYPE_PUSH with a non bsp model");
-
- return model.headnode;
- }
-
- // create a temp hull from bounding box sizes
-
- return CM.HeadnodeForBox(ent.mins, ent.maxs);
- }
-
- //===========================================================================
-
- /*
- ====================
- SV_ClipMoveToEntities
-
- ====================
- */
- private static edict_t touchlist[]= new edict_t[MAX_EDICTS];
- public static void SV_ClipMoveToEntities(moveclip_t clip)
- {
- int i, num;
-
- edict_t touch;
- trace_t trace;
- int headnode;
- float angles[];
-
- num= SV_AreaEdicts(clip.boxmins, clip.boxmaxs, touchlist, MAX_EDICTS, AREA_SOLID);
-
- // be careful, it is possible to have an entity in this
- // list removed before we get to it (killtriggered)
- for (i= 0; i < num; i++)
- {
- touch= touchlist[i];
- if (touch.solid == SOLID_NOT)
- continue;
- if (touch == clip.passedict)
- continue;
- if (clip.trace.allsolid)
- return;
- if (clip.passedict != null)
- {
- if (touch.owner == clip.passedict)
- continue; // don't clip against own missiles
- if (clip.passedict.owner == touch)
- continue; // don't clip against owner
- }
-
- if (0 == (clip.contentmask & CONTENTS_DEADMONSTER) && 0 != (touch.svflags & SVF_DEADMONSTER))
- continue;
-
- // might intersect, so do an exact clip
- headnode= SV_HullForEntity(touch);
- angles= touch.s.angles;
- if (touch.solid != SOLID_BSP)
- angles= vec3_origin; // boxes don't rotate
-
- if ((touch.svflags & SVF_MONSTER) != 0)
- trace=
- CM.TransformedBoxTrace(
- clip.start,
- clip.end,
- clip.mins2,
- clip.maxs2,
- headnode,
- clip.contentmask,
- touch.s.origin,
- angles);
- else
- trace=
- CM.TransformedBoxTrace(
- clip.start,
- clip.end,
- clip.mins,
- clip.maxs,
- headnode,
- clip.contentmask,
- touch.s.origin,
- angles);
-
- if (trace.allsolid || trace.startsolid || trace.fraction < clip.trace.fraction)
- {
- trace.ent= touch;
- if (clip.trace.startsolid)
- {
- clip.trace= trace;
- clip.trace.startsolid= true;
- }
- else
- clip.trace.set(trace);
- }
- else if (trace.startsolid)
- clip.trace.startsolid= true;
- }
- }
-
- /*
- ==================
- SV_TraceBounds
- ==================
- */
- public static void SV_TraceBounds(float[] start, float[] mins, float[] maxs, float[] end, float[] boxmins, float[] boxmaxs)
- {
- int i;
-
- for (i= 0; i < 3; i++)
- {
- if (end[i] > start[i])
- {
- boxmins[i]= start[i] + mins[i] - 1;
- boxmaxs[i]= end[i] + maxs[i] + 1;
- }
- else
- {
- boxmins[i]= end[i] + mins[i] - 1;
- boxmaxs[i]= start[i] + maxs[i] + 1;
- }
- }
-
- }
-
- /*
- ==================
- SV_Trace
-
- Moves the given mins/maxs volume through the world from start to end.
-
- Passedict and edicts owned by passedict are explicitly not checked.
-
- ==================
- */
- public static trace_t SV_Trace(float[] start, float[] mins, float[] maxs, float[] end, edict_t passedict, int contentmask)
- {
- moveclip_t clip= new moveclip_t();
-
- if (mins == null)
- mins= vec3_origin;
- if (maxs == null)
- maxs= vec3_origin;
-
- //memset ( clip, 0, sizeof ( moveclip_t ) );
-
- // clip to world
- clip.trace= CM.BoxTrace(start, end, mins, maxs, 0, contentmask);
- clip.trace.ent= GameBase.g_edicts[0];
- if (clip.trace.fraction == 0)
- return clip.trace; // blocked by the world
-
- clip.contentmask= contentmask;
- clip.start= start;
- clip.end= end;
- clip.mins= mins;
- clip.maxs= maxs;
- clip.passedict= passedict;
-
- VectorCopy(mins, clip.mins2);
- VectorCopy(maxs, clip.maxs2);
-
- // create the bounding box of the entire move
- SV_TraceBounds(start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs);
-
- // clip to other solid entities
- SV_ClipMoveToEntities(clip);
-
- return clip.trace;
- }
-}
+import jake2.util.Math3D;
+
+public class SV_WORLD {
+ // world.c -- world query functions
+ //
+ //
+ //===============================================================================
+ //
+ //ENTITY AREA CHECKING
+ //
+ //FIXME: this use of "area" is different from the bsp file use
+ //===============================================================================
+ public static areanode_t sv_areanodes[] = new areanode_t[Defines.AREA_NODES];
+ static {
+ SV_WORLD.initNodes();
+ }
+
+ public static int sv_numareanodes;
+
+ public static float area_mins[], area_maxs[];
+
+ public static edict_t area_list[];
+
+ public static int area_count, area_maxcount;
+
+ public static int area_type;
+
+ public static final int MAX_TOTAL_ENT_LEAFS = 128;
+
+ static int leafs[] = new int[MAX_TOTAL_ENT_LEAFS];
+
+ static int clusters[] = new int[MAX_TOTAL_ENT_LEAFS];
+
+ //===========================================================================
+ static edict_t touch[] = new edict_t[Defines.MAX_EDICTS];
+
+ //===========================================================================
+ static edict_t touchlist[] = new edict_t[Defines.MAX_EDICTS];
+
+ public static void initNodes() {
+ for (int n = 0; n < Defines.AREA_NODES; n++)
+ SV_WORLD.sv_areanodes[n] = new areanode_t();
+ }
+
+ // ClearLink is used for new headnodes
+ public static void ClearLink(link_t l) {
+ l.prev = l.next = l;
+ }
+
+ public static void RemoveLink(link_t l) {
+ l.next.prev = l.prev;
+ l.prev.next = l.next;
+ }
+
+ public static void InsertLinkBefore(link_t l, link_t before) {
+ l.next = before;
+ l.prev = before.prev;
+ l.prev.next = l;
+ l.next.prev = l;
+ }
+
+ /*
+ * =============== SV_CreateAreaNode
+ *
+ * Builds a uniformly subdivided tree for the given world size
+ * ===============
+ */
+ public static areanode_t SV_CreateAreaNode(int depth, float[] mins,
+ float[] maxs) {
+ areanode_t anode;
+ float[] size = { 0, 0, 0 };
+ float[] mins1 = { 0, 0, 0 }, maxs1 = { 0, 0, 0 }, mins2 = { 0, 0, 0 }, maxs2 = {
+ 0, 0, 0 };
+ anode = SV_WORLD.sv_areanodes[SV_WORLD.sv_numareanodes];
+ // just for debugging (rst)
+ Math3D.VectorCopy(mins, anode.mins_rst);
+ Math3D.VectorCopy(maxs, anode.maxs_rst);
+ SV_WORLD.sv_numareanodes++;
+ ClearLink(anode.trigger_edicts);
+ ClearLink(anode.solid_edicts);
+ if (depth == Defines.AREA_DEPTH) {
+ anode.axis = -1;
+ anode.children[0] = anode.children[1] = null;
+ return anode;
+ }
+ Math3D.VectorSubtract(maxs, mins, size);
+ if (size[0] > size[1])
+ anode.axis = 0;
+ else
+ anode.axis = 1;
+ anode.dist = 0.5f * (maxs[anode.axis] + mins[anode.axis]);
+ Math3D.VectorCopy(mins, mins1);
+ Math3D.VectorCopy(mins, mins2);
+ Math3D.VectorCopy(maxs, maxs1);
+ Math3D.VectorCopy(maxs, maxs2);
+ maxs1[anode.axis] = mins2[anode.axis] = anode.dist;
+ anode.children[0] = SV_CreateAreaNode(depth + 1, mins2, maxs2);
+ anode.children[1] = SV_CreateAreaNode(depth + 1, mins1, maxs1);
+ return anode;
+ }
+
+ /*
+ * =============== SV_ClearWorld
+ *
+ * ===============
+ */
+ public static void SV_ClearWorld() {
+ initNodes();
+ SV_WORLD.sv_numareanodes = 0;
+ SV_CreateAreaNode(0, SV_INIT.sv.models[1].mins,
+ SV_INIT.sv.models[1].maxs);
+ /*
+ * Com.p("areanodes:" + sv_numareanodes + " (sollten 32 sein)."); for
+ * (int n = 0; n < sv_numareanodes; n++) { Com.Printf( "|%3i|%2i|%8.2f
+ * |%8.2f|%8.2f|%8.2f| %8.2f|%8.2f|%8.2f|\n", new Vargs() .add(n)
+ * .add(sv_areanodes[n].axis) .add(sv_areanodes[n].dist)
+ * .add(sv_areanodes[n].mins_rst[0]) .add(sv_areanodes[n].mins_rst[1])
+ * .add(sv_areanodes[n].mins_rst[2]) .add(sv_areanodes[n].maxs_rst[0])
+ * .add(sv_areanodes[n].maxs_rst[1]) .add(sv_areanodes[n].maxs_rst[2])); }
+ */
+ }
+
+ /*
+ * =============== SV_UnlinkEdict ===============
+ */
+ public static void SV_UnlinkEdict(edict_t ent) {
+ if (null == ent.area.prev)
+ return; // not linked in anywhere
+ RemoveLink(ent.area);
+ ent.area.prev = ent.area.next = null;
+ }
+
+ public static void SV_LinkEdict(edict_t ent) {
+ areanode_t node;
+ int num_leafs;
+ int j, k;
+ int area;
+ int topnode = 0;
+ if (ent.area.prev != null)
+ SV_UnlinkEdict(ent); // unlink from old position
+ if (ent == GameBase.g_edicts[0])
+ return; // don't add the world
+ if (!ent.inuse)
+ return;
+ // set the size
+ Math3D.VectorSubtract(ent.maxs, ent.mins, ent.size);
+ // encode the size into the entity_state for client prediction
+ if (ent.solid == Defines.SOLID_BBOX
+ && 0 == (ent.svflags & Defines.SVF_DEADMONSTER)) {
+ // assume that x/y are equal and symetric
+ int i = (int) (ent.maxs[0] / 8);
+ if (i < 1)
+ i = 1;
+ if (i > 31)
+ i = 31;
+ // z is not symetric
+ j = (int) ((-ent.mins[2]) / 8);
+ if (j < 1)
+ j = 1;
+ if (j > 31)
+ j = 31;
+ // and z maxs can be negative...
+ k = (int) ((ent.maxs[2] + 32) / 8);
+ if (k < 1)
+ k = 1;
+ if (k > 63)
+ k = 63;
+ ent.s.solid = (k << 10) | (j << 5) | i;
+ } else if (ent.solid == Defines.SOLID_BSP) {
+ ent.s.solid = 31; // a solid_bbox will never create this value
+ } else
+ ent.s.solid = 0;
+ // set the abs box
+ if (ent.solid == Defines.SOLID_BSP
+ && (ent.s.angles[0] != 0 || ent.s.angles[1] != 0 || ent.s.angles[2] != 0)) {
+ // expand for rotation
+ float max, v;
+ max = 0;
+ for (int i = 0; i < 3; i++) {
+ v = Math.abs(ent.mins[i]);
+ if (v > max)
+ max = v;
+ v = Math.abs(ent.maxs[i]);
+ if (v > max)
+ max = v;
+ }
+ for (int i = 0; i < 3; i++) {
+ ent.absmin[i] = ent.s.origin[i] - max;
+ ent.absmax[i] = ent.s.origin[i] + max;
+ }
+ } else {
+ // normal
+ Math3D.VectorAdd(ent.s.origin, ent.mins, ent.absmin);
+ Math3D.VectorAdd(ent.s.origin, ent.maxs, ent.absmax);
+ }
+ // because movement is clipped an epsilon away from an actual edge,
+ // we must fully check even when bounding boxes don't quite touch
+ ent.absmin[0]--;
+ ent.absmin[1]--;
+ ent.absmin[2]--;
+ ent.absmax[0]++;
+ ent.absmax[1]++;
+ ent.absmax[2]++;
+ // link to PVS leafs
+ ent.num_clusters = 0;
+ ent.areanum = 0;
+ ent.areanum2 = 0;
+ // get all leafs, including solids
+ int iw[] = { topnode };
+ num_leafs = CM.CM_BoxLeafnums(ent.absmin, ent.absmax, SV_WORLD.leafs,
+ SV_WORLD.MAX_TOTAL_ENT_LEAFS, iw);
+ topnode = iw[0];
+ // set areas
+ for (int i = 0; i < num_leafs; i++) {
+ SV_WORLD.clusters[i] = CM.CM_LeafCluster(SV_WORLD.leafs[i]);
+ area = CM.CM_LeafArea(SV_WORLD.leafs[i]);
+ if (area != 0) {
+ // doors may legally straggle two areas,
+ // but nothing should evern need more than that
+ if (ent.areanum != 0 && ent.areanum != area) {
+ if (ent.areanum2 != 0 && ent.areanum2 != area
+ && SV_INIT.sv.state == Defines.ss_loading)
+ Com.DPrintf("Object touching 3 areas at "
+ + ent.absmin[0] + " " + ent.absmin[1] + " "
+ + ent.absmin[2] + "\n");
+ ent.areanum2 = area;
+ } else
+ ent.areanum = area;
+ }
+ }
+ if (num_leafs >= SV_WORLD.MAX_TOTAL_ENT_LEAFS) {
+ // assume we missed some leafs, and mark by headnode
+ ent.num_clusters = -1;
+ ent.headnode = topnode;
+ } else {
+ ent.num_clusters = 0;
+ for (int i = 0; i < num_leafs; i++) {
+ if (SV_WORLD.clusters[i] == -1)
+ continue; // not a visible leaf
+ for (j = 0; j < i; j++)
+ if (SV_WORLD.clusters[j] == SV_WORLD.clusters[i])
+ break;
+ if (j == i) {
+ if (ent.num_clusters == Defines.MAX_ENT_CLUSTERS) {
+ // assume we missed some leafs, and mark by headnode
+ ent.num_clusters = -1;
+ ent.headnode = topnode;
+ break;
+ }
+ ent.clusternums[ent.num_clusters++] = SV_WORLD.clusters[i];
+ }
+ }
+ }
+ // if first time, make sure old_origin is valid
+ if (0 == ent.linkcount) {
+ Math3D.VectorCopy(ent.s.origin, ent.s.old_origin);
+ }
+ ent.linkcount++;
+ if (ent.solid == Defines.SOLID_NOT)
+ return;
+ // find the first node that the ent's box crosses
+ node = SV_WORLD.sv_areanodes[0];
+ while (true) {
+ if (node.axis == -1)
+ break;
+ if (ent.absmin[node.axis] > node.dist)
+ node = node.children[0];
+ else if (ent.absmax[node.axis] < node.dist)
+ node = node.children[1];
+ else
+ break; // crosses the node
+ }
+ // link it in
+ if (ent.solid == Defines.SOLID_TRIGGER)
+ InsertLinkBefore(ent.area, node.trigger_edicts);
+ else
+ InsertLinkBefore(ent.area, node.solid_edicts);
+ }
+
+ /*
+ * ==================== SV_AreaEdicts_r
+ *
+ * ====================
+ */
+ public static void SV_AreaEdicts_r(areanode_t node) {
+ link_t l, next, start;
+ edict_t check;
+ int count;
+ count = 0;
+ // touch linked edicts
+ if (SV_WORLD.area_type == Defines.AREA_SOLID)
+ start = node.solid_edicts;
+ else
+ start = node.trigger_edicts;
+ for (l = start.next; l != start; l = next) {
+ next = l.next;
+ check = (edict_t) l.o;
+ if (check.solid == Defines.SOLID_NOT)
+ continue; // deactivated
+ if (check.absmin[0] > SV_WORLD.area_maxs[0]
+ || check.absmin[1] > SV_WORLD.area_maxs[1]
+ || check.absmin[2] > SV_WORLD.area_maxs[2]
+ || check.absmax[0] < SV_WORLD.area_mins[0]
+ || check.absmax[1] < SV_WORLD.area_mins[1]
+ || check.absmax[2] < SV_WORLD.area_mins[2])
+ continue; // not touching
+ if (SV_WORLD.area_count == SV_WORLD.area_maxcount) {
+ Com.Printf("SV_AreaEdicts: MAXCOUNT\n");
+ return;
+ }
+ SV_WORLD.area_list[SV_WORLD.area_count] = check;
+ SV_WORLD.area_count++;
+ }
+ if (node.axis == -1)
+ return; // terminal node
+ // recurse down both sides
+ if (SV_WORLD.area_maxs[node.axis] > node.dist)
+ SV_AreaEdicts_r(node.children[0]);
+ if (SV_WORLD.area_mins[node.axis] < node.dist)
+ SV_AreaEdicts_r(node.children[1]);
+ }
+
+ /*
+ * ================ SV_AreaEdicts ================
+ */
+ public static int SV_AreaEdicts(float[] mins, float[] maxs, edict_t list[],
+ int maxcount, int areatype) {
+ SV_WORLD.area_mins = mins;
+ SV_WORLD.area_maxs = maxs;
+ SV_WORLD.area_list = list;
+ SV_WORLD.area_count = 0;
+ SV_WORLD.area_maxcount = maxcount;
+ SV_WORLD.area_type = areatype;
+ SV_AreaEdicts_r(SV_WORLD.sv_areanodes[0]);
+ return SV_WORLD.area_count;
+ }
+
+ /*
+ * ============= SV_PointContents =============
+ */
+ public static int SV_PointContents(float[] p) {
+ edict_t hit;
+ int i, num;
+ int contents, c2;
+ int headnode;
+ float angles[];
+ // get base contents from world
+ contents = CM.PointContents(p, SV_INIT.sv.models[1].headnode);
+ // or in contents from all the other entities
+ num = SV_AreaEdicts(p, p, SV_WORLD.touch, Defines.MAX_EDICTS,
+ Defines.AREA_SOLID);
+ for (i = 0; i < num; i++) {
+ hit = SV_WORLD.touch[i];
+ // might intersect, so do an exact clip
+ headnode = SV_HullForEntity(hit);
+ angles = hit.s.angles;
+ if (hit.solid != Defines.SOLID_BSP)
+ angles = Globals.vec3_origin; // boxes don't rotate
+ c2 = CM.TransformedPointContents(p, headnode, hit.s.origin,
+ hit.s.angles);
+ contents |= c2;
+ }
+ return contents;
+ }
+
+ /*
+ * ================ SV_HullForEntity
+ *
+ * Returns a headnode that can be used for testing or clipping an object of
+ * mins/maxs size. Offset is filled in to contain the adjustment that must
+ * be added to the testing object's origin to get a point to use with the
+ * returned hull. ================
+ */
+ public static int SV_HullForEntity(edict_t ent) {
+ cmodel_t model;
+ // decide which clipping hull to use, based on the size
+ if (ent.solid == Defines.SOLID_BSP) {
+ // explicit hulls in the BSP model
+ model = SV_INIT.sv.models[ent.s.modelindex];
+ if (null == model)
+ Com.Error(Defines.ERR_FATAL,
+ "MOVETYPE_PUSH with a non bsp model");
+ return model.headnode;
+ }
+ // create a temp hull from bounding box sizes
+ return CM.HeadnodeForBox(ent.mins, ent.maxs);
+ }
+
+ public static void SV_ClipMoveToEntities(moveclip_t clip) {
+ int i, num;
+ edict_t touch;
+ trace_t trace;
+ int headnode;
+ float angles[];
+ num = SV_AreaEdicts(clip.boxmins, clip.boxmaxs, SV_WORLD.touchlist,
+ Defines.MAX_EDICTS, Defines.AREA_SOLID);
+ // be careful, it is possible to have an entity in this
+ // list removed before we get to it (killtriggered)
+ for (i = 0; i < num; i++) {
+ touch = SV_WORLD.touchlist[i];
+ if (touch.solid == Defines.SOLID_NOT)
+ continue;
+ if (touch == clip.passedict)
+ continue;
+ if (clip.trace.allsolid)
+ return;
+ if (clip.passedict != null) {
+ if (touch.owner == clip.passedict)
+ continue; // don't clip against own missiles
+ if (clip.passedict.owner == touch)
+ continue; // don't clip against owner
+ }
+ if (0 == (clip.contentmask & Defines.CONTENTS_DEADMONSTER)
+ && 0 != (touch.svflags & Defines.SVF_DEADMONSTER))
+ continue;
+ // might intersect, so do an exact clip
+ headnode = SV_HullForEntity(touch);
+ angles = touch.s.angles;
+ if (touch.solid != Defines.SOLID_BSP)
+ angles = Globals.vec3_origin; // boxes don't rotate
+ if ((touch.svflags & Defines.SVF_MONSTER) != 0)
+ trace = CM.TransformedBoxTrace(clip.start, clip.end,
+ clip.mins2, clip.maxs2, headnode, clip.contentmask,
+ touch.s.origin, angles);
+ else
+ trace = CM.TransformedBoxTrace(clip.start, clip.end, clip.mins,
+ clip.maxs, headnode, clip.contentmask, touch.s.origin,
+ angles);
+ if (trace.allsolid || trace.startsolid
+ || trace.fraction < clip.trace.fraction) {
+ trace.ent = touch;
+ if (clip.trace.startsolid) {
+ clip.trace = trace;
+ clip.trace.startsolid = true;
+ } else
+ clip.trace.set(trace);
+ } else if (trace.startsolid)
+ clip.trace.startsolid = true;
+ }
+ }
+
+ /*
+ * ================== SV_TraceBounds ==================
+ */
+ public static void SV_TraceBounds(float[] start, float[] mins,
+ float[] maxs, float[] end, float[] boxmins, float[] boxmaxs) {
+ int i;
+ for (i = 0; i < 3; i++) {
+ if (end[i] > start[i]) {
+ boxmins[i] = start[i] + mins[i] - 1;
+ boxmaxs[i] = end[i] + maxs[i] + 1;
+ } else {
+ boxmins[i] = end[i] + mins[i] - 1;
+ boxmaxs[i] = start[i] + maxs[i] + 1;
+ }
+ }
+ }
+
+ /*
+ * ================== SV_Trace
+ *
+ * Moves the given mins/maxs volume through the world from start to end.
+ *
+ * Passedict and edicts owned by passedict are explicitly not checked.
+ *
+ * ==================
+ */
+ public static trace_t SV_Trace(float[] start, float[] mins, float[] maxs,
+ float[] end, edict_t passedict, int contentmask) {
+ moveclip_t clip = new moveclip_t();
+ if (mins == null)
+ mins = Globals.vec3_origin;
+ if (maxs == null)
+ maxs = Globals.vec3_origin;
+
+ // clip to world
+ clip.trace = CM.BoxTrace(start, end, mins, maxs, 0, contentmask);
+ clip.trace.ent = GameBase.g_edicts[0];
+ if (clip.trace.fraction == 0)
+ return clip.trace; // blocked by the world
+ clip.contentmask = contentmask;
+ clip.start = start;
+ clip.end = end;
+ clip.mins = mins;
+ clip.maxs = maxs;
+ clip.passedict = passedict;
+ Math3D.VectorCopy(mins, clip.mins2);
+ Math3D.VectorCopy(maxs, clip.maxs2);
+ // create the bounding box of the entire move
+ SV_TraceBounds(start, clip.mins2, clip.maxs2, end, clip.boxmins,
+ clip.boxmaxs);
+ // clip to other solid entities
+ SV_ClipMoveToEntities(clip);
+ return clip.trace;
+ }
+} \ No newline at end of file
diff --git a/src/jake2/server/server_static_t.java b/src/jake2/server/server_static_t.java
index c9f94a2..fffc9e9 100644
--- a/src/jake2/server/server_static_t.java
+++ b/src/jake2/server/server_static_t.java
@@ -1,65 +1,71 @@
/*
-Copyright (C) 1997-2001 Id Software, Inc.
+ * 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.
+ *
+ */
-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.
+// Created on 14.01.2004 by RST.
+// $Id: server_static_t.java,v 1.2 2004-09-22 19:22:12 salomo Exp $
+package jake2.server;
-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.
+import jake2.Defines;
+import jake2.game.entity_state_t;
+import jake2.qcommon.sizebuf_t;
-See the GNU General Public License for more details.
+import java.io.RandomAccessFile;
-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.
+public class server_static_t {
+ public server_static_t() {
+ for (int n = 0; n < Defines.MAX_CHALLENGES; n++) {
+ challenges[n] = new challenge_t();
+ }
+ }
-*/
+ boolean initialized; // sv_init has completed
-// Created on 14.01.2004 by RST.
-// $Id: server_static_t.java,v 1.1 2004-07-07 19:59:51 hzi Exp $
+ int realtime; // always increasing, no clamping, etc
-package jake2.server;
+ String mapcmd = ""; // ie: *intro.cin+base
-import jake2.*;
-import jake2.client.*;
-import jake2.game.*;
-import jake2.qcommon.*;
-import jake2.render.*;
-import jake2.server.*;
+ int spawncount; // incremented each server start
-import java.io.*;
+ // used to check late spawns
-public class server_static_t {
- public server_static_t() {
- for (int n = 0; n < Defines.MAX_CHALLENGES; n++) {
- challenges[n] = new challenge_t();
- }
- }
+ client_t clients[]; // [maxclients->value];
- boolean initialized; // sv_init has completed
- int realtime; // always increasing, no clamping, etc
+ int num_client_entities; // maxclients->value*UPDATE_BACKUP*MAX_PACKET_ENTITIES
- //char mapcmd[MAX_TOKEN_CHARS]; // ie: *intro.cin+base
- String mapcmd = ""; // ie: *intro.cin+base
+ int next_client_entities; // next client_entity to use
- int spawncount; // incremented each server start
- // used to check late spawns
+ entity_state_t client_entities[]; // [num_client_entities]
- client_t clients[]; // [maxclients->value];
+ int last_heartbeat;
- int num_client_entities; // maxclients->value*UPDATE_BACKUP*MAX_PACKET_ENTITIES
- int next_client_entities; // next client_entity to use
- entity_state_t client_entities[]; // [num_client_entities]
+ challenge_t challenges[] = new challenge_t[Defines.MAX_CHALLENGES]; // to
+ // prevent
+ // invalid
+ // IPs
+ // from
+ // connecting
- int last_heartbeat;
+ // serverrecord values
+ RandomAccessFile demofile;
- challenge_t challenges[] = new challenge_t[Defines.MAX_CHALLENGES]; // to prevent invalid IPs from connecting
+ sizebuf_t demo_multicast = new sizebuf_t();
- // serverrecord values
- RandomAccessFile demofile;
- sizebuf_t demo_multicast = new sizebuf_t();
- byte demo_multicast_buf[] = new byte[Defines.MAX_MSGLEN];
-}
+ byte demo_multicast_buf[] = new byte[Defines.MAX_MSGLEN];
+} \ No newline at end of file
diff --git a/src/jake2/server/server_t.java b/src/jake2/server/server_t.java
index 8e260a2..d67e384 100644
--- a/src/jake2/server/server_t.java
+++ b/src/jake2/server/server_t.java
@@ -1,76 +1,72 @@
/*
-Copyright (C) 1997-2001 Id Software, Inc.
+ * 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.
+ *
+ */
-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.
+// Created on 14.01.2004 by RST.
+// $Id: server_t.java,v 1.2 2004-09-22 19:22:12 salomo Exp $
+package jake2.server;
-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.
+import jake2.Defines;
+import jake2.game.cmodel_t;
+import jake2.game.entity_state_t;
+import jake2.qcommon.sizebuf_t;
-See the GNU General Public License for more details.
+import java.io.RandomAccessFile;
-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.
+public class server_t {
-*/
+ public server_t() {
+ models = new cmodel_t[Defines.MAX_MODELS];
+ for (int n = 0; n < Defines.MAX_MODELS; n++)
+ models[n] = new cmodel_t();
-// Created on 14.01.2004 by RST.
-// $Id: server_t.java,v 1.1 2004-07-07 19:59:51 hzi Exp $
+ for (int n = 0; n < Defines.MAX_EDICTS; n++)
+ baselines[n] = new entity_state_t(null);
+ }
-package jake2.server;
+ int state; // precache commands are only valid during load
-import java.io.File;
-import java.io.RandomAccessFile;
+ boolean attractloop; // running cinematics and demos for the local system
+ // only
-import jake2.*;
-import jake2.client.*;
-import jake2.game.*;
-import jake2.qcommon.*;
-import jake2.render.*;
+ boolean loadgame; // client begins should reuse existing entity
-public class server_t {
+ int time; // always sv.framenum * 100 msec
+
+ int framenum;
+
+ String name = ""; // map name, or cinematic name
+
+ cmodel_t models[];
+
+ String configstrings[] = new String[Defines.MAX_CONFIGSTRINGS];
+
+ entity_state_t baselines[] = new entity_state_t[Defines.MAX_EDICTS];
+
+ // the multicast buffer is used to send a message to a set of clients
+ // it is only used to marshall data until SV_Multicast is called
+ sizebuf_t multicast = new sizebuf_t();
+
+ byte multicast_buf[] = new byte[Defines.MAX_MSGLEN];
+
+ // demo server information
+ RandomAccessFile demofile;
- public server_t() {
- models = new cmodel_t[Defines.MAX_MODELS];
- for (int n = 0; n < Defines.MAX_MODELS; n++)
- models[n] = new cmodel_t();
-
- for (int n=0; n < Defines.MAX_EDICTS; n++)
- baselines[n] = new entity_state_t(null);
-
- //for (int n=0; n < Defines.MAX_CONFIGSTRINGS; n++)
- //configstrings[n] = "";
-
- }
- //server_state_t state;
- int state; // precache commands are only valid during load
-
- boolean attractloop; // running cinematics and demos for the local system only
- boolean loadgame; // client begins should reuse existing entity
-
- int time; // always sv.framenum * 100 msec
- int framenum;
-
- //char name[MAX_QPATH];
- String name = ""; // map name, or cinematic name
-
- //struct cmodel_s *models[MAX_MODELS];
- cmodel_t models[];
-
- //char configstrings[MAX_CONFIGSTRINGS][MAX_QPATH];
- String configstrings[] = new String[Defines.MAX_CONFIGSTRINGS];
- entity_state_t baselines[] = new entity_state_t[Defines.MAX_EDICTS];
-
- // the multicast buffer is used to send a message to a set of clients
- // it is only used to marshall data until SV_Multicast is called
- sizebuf_t multicast = new sizebuf_t();
- byte multicast_buf[] = new byte[Defines.MAX_MSGLEN];
-
- // demo server information
- RandomAccessFile demofile;
- boolean timedemo; // don't time sync
-}
+ boolean timedemo; // don't time sync
+} \ No newline at end of file