SV_USER.java :  » Game » jake2-ext » jake2 » server » Java Open Source

Java Open Source » Game » jake2 ext 
jake2 ext » jake2 » server » SV_USER.java
/*
 * 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.10 2005/12/17 20:32:01 salomo Exp $
package jake2.server;

import jake2.Defines;
import jake2.Globals;
import jake2.game.Cmd;
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 {

  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);

    // rst: this handles loading errors, no message yet visible 
    if (SV_MAIN.sv_client.download == null) {
      return;
    }

    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) {

    Com.dprintln("SV_ExecuteUserCommand:" + 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 (Cmd.Argv(0).equals(u.name)) {
        u.r.run();
        break;
      }
    }

    if (i == SV_USER.ucmds.length && SV_INIT.sv.state == Defines.ss_game) {
      Cmd.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;
      }
    }
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.