summaryrefslogtreecommitdiffstats
path: root/src/jake2/client/CL_parse.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jake2/client/CL_parse.java')
-rw-r--r--src/jake2/client/CL_parse.java802
1 files changed, 802 insertions, 0 deletions
diff --git a/src/jake2/client/CL_parse.java b/src/jake2/client/CL_parse.java
new file mode 100644
index 0000000..e972ea6
--- /dev/null
+++ b/src/jake2/client/CL_parse.java
@@ -0,0 +1,802 @@
+/*
+ * CL_parse.java
+ * Copyright (C) 2004
+ *
+ * $Id: CL_parse.java,v 1.1 2004-07-07 19:58:38 hzi Exp $
+ */
+/*
+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.
+
+*/
+package jake2.client;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import jake2.Defines;
+import jake2.game.Cmd;
+import jake2.game.entity_state_t;
+import jake2.qcommon.CM;
+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.qcommon.xcommand_t;
+import jake2.render.model_t;
+import jake2.sys.Sys;
+import jake2.util.Lib;
+
+/**
+ * CL_parse
+ */
+public class CL_parse extends CL_view {
+
+ //// cl_parse.c -- parse a message received from the server
+
+ public static String svc_strings[] =
+ {
+ "svc_bad",
+ "svc_muzzleflash",
+ "svc_muzzlflash2",
+ "svc_temp_entity",
+ "svc_layout",
+ "svc_inventory",
+ "svc_nop",
+ "svc_disconnect",
+ "svc_reconnect",
+ "svc_sound",
+ "svc_print",
+ "svc_stufftext",
+ "svc_serverdata",
+ "svc_configstring",
+ "svc_spawnbaseline",
+ "svc_centerprint",
+ "svc_download",
+ "svc_playerinfo",
+ "svc_packetentities",
+ "svc_deltapacketentities",
+ "svc_frame" };
+
+ // =============================================================================
+
+ public static String DownloadFileName(String fn) {
+ if ("players".equals(fn))
+ return BASEDIRNAME + "/" + fn;
+ else
+ return FS.Gamedir() + "/" + fn;
+ }
+
+ /*
+ ===============
+ CL_CheckOrDownloadFile
+
+ Returns true if the file exists, otherwise it attempts
+ to start a download from the server.
+ ===============
+ */
+ public static boolean CheckOrDownloadFile(String filename) {
+ RandomAccessFile fp;
+ String name;
+
+ if (filename.indexOf("..") != -1) {
+ Com.Printf("Refusing to download a path with ..\n");
+ return true;
+ }
+
+ if (FS.LoadFile(filename) != null) { // it exists, no need to download
+ return true;
+ }
+
+ cls.downloadname = filename;
+
+ // download to a temp name, and only rename
+ // to the real name when done, so if interrupted
+ // a runt file wont be left
+ Com.StripExtension(cls.downloadname, cls.downloadtempname);
+ cls.downloadtempname += ".tmp";
+
+ // ZOID
+ // check to see if we already have a tmp for this file, if so, try to resume
+ // open the file if not opened yet
+ name = DownloadFileName(cls.downloadtempname);
+
+ fp = fopen(name, "r+b");
+ if (fp != null) { // it exists
+ long len = 0;
+
+ try {
+ len = fp.length();
+ }
+ catch (IOException e) {
+ };
+
+ cls.download = fp;
+
+ // give the server an offset to start the download
+ Com.Printf("Resuming " + cls.downloadname + "\n");
+ MSG.WriteByte(cls.netchan.message, clc_stringcmd);
+ MSG.WriteString(cls.netchan.message, "download " + cls.downloadname + " " + len);
+ }
+ else {
+ // TODO bugfix cwei
+ cls.downloadname = cls.downloadname.toLowerCase();
+
+ Com.Printf("Downloading " + cls.downloadname + "\n");
+ MSG.WriteByte(cls.netchan.message, clc_stringcmd);
+ MSG.WriteString(cls.netchan.message, "download " + cls.downloadname);
+ }
+
+ cls.downloadnumber++;
+
+ return false;
+ }
+
+ /*
+ ===============
+ CL_Download_f
+
+ Request a download from the server
+ ===============
+ */
+ static xcommand_t Download_f = new xcommand_t() {
+ public void execute() {
+ String filename;
+
+ if (Cmd.Argc() != 2) {
+ Com.Printf("Usage: download <filename>\n");
+ return;
+ }
+
+ filename = Cmd.Argv(1);
+
+ if (strstr(filename, "..")) {
+ Com.Printf("Refusing to download a path with ..\n");
+ return;
+ }
+
+ if (FS.LoadFile(filename) != null) { // it exists, no need to download
+ Com.Printf("File already exists.\n");
+ return;
+ }
+
+ cls.downloadname = filename;
+ Com.Printf("Downloading " + cls.downloadname + "\n");
+
+ // download to a temp name, and only rename
+ // to the real name when done, so if interrupted
+ // a runt file wont be left
+ Com.StripExtension(cls.downloadname, cls.downloadtempname);
+ strcat(cls.downloadtempname, ".tmp");
+
+ MSG.WriteByte(cls.netchan.message, clc_stringcmd);
+ MSG.WriteString(cls.netchan.message, "download " + cls.downloadname);
+
+ cls.downloadnumber++;
+ }
+ };
+
+ /*
+ ======================
+ CL_RegisterSounds
+ ======================
+ */
+ static void RegisterSounds() {
+ int i;
+ S.BeginRegistration();
+ CL.RegisterTEntSounds();
+ for (i = 1; i < MAX_SOUNDS; i++) {
+ if (cl.configstrings[CS_SOUNDS + i] == null || cl.configstrings[CS_SOUNDS + i] == "")
+ break;
+ cl.sound_precache[i] = S.RegisterSound(cl.configstrings[CS_SOUNDS + i]);
+ Sys.SendKeyEvents(); // pump message loop
+ }
+ S.EndRegistration();
+ }
+
+ /*
+ =====================
+ CL_ParseDownload
+
+ A download message has been received from the server
+ =====================
+ */
+ public static void ParseDownload() {
+ int size, percent;
+ String name;
+ int r;
+
+ // read the data
+ size = MSG.ReadShort(net_message);
+ percent = MSG.ReadByte(net_message);
+ if (size == -1) {
+ Com.Printf("Server does not have this file.\n");
+ if (cls.download != null) {
+ // if here, we tried to resume a file but the server said no
+ fclose(cls.download);
+ cls.download = null;
+ }
+ CL.RequestNextDownload();
+ return;
+ }
+
+ // open the file if not opened yet
+ if (cls.download == null) {
+ name = DownloadFileName(cls.downloadtempname);
+
+ FS.CreatePath(name);
+
+ cls.download = fopen(name, "rw");
+ if (cls.download == null) {
+ net_message.readcount += size;
+ Com.Printf("Failed to open " + cls.downloadtempname + "\n");
+ CL.RequestNextDownload();
+ return;
+ }
+ }
+
+ //fwrite(net_message.data[net_message.readcount], 1, size, cls.download);
+ try {
+ cls.download.write(net_message.data, net_message.readcount, size);
+ }
+ catch (Exception e) {
+ }
+ net_message.readcount += size;
+
+ if (percent != 100) {
+ // request next block
+ // change display routines by zoid
+
+ MSG.WriteByte(cls.netchan.message, clc_stringcmd);
+ SZ.Print(cls.netchan.message, "nextdl");
+ }
+ else {
+ String oldn, newn;
+ //char oldn[MAX_OSPATH];
+ //char newn[MAX_OSPATH];
+
+ // Com.Printf ("100%%\n");
+
+ fclose(cls.download);
+
+ // rename the temp file to it's final name
+ oldn = DownloadFileName(cls.downloadtempname);
+ newn = DownloadFileName(cls.downloadname);
+ r = Lib.rename(oldn, newn);
+ if (r != 0)
+ Com.Printf("failed to rename.\n");
+
+ cls.download = null;
+ cls.downloadpercent = 0;
+
+ // get another file if needed
+
+ CL.RequestNextDownload();
+ }
+ }
+
+ /*
+ =====================================================================
+
+ SERVER CONNECTING MESSAGES
+
+ =====================================================================
+ */
+
+ /*
+ ==================
+ CL_ParseServerData
+ ==================
+ */
+ //checked once, was ok.
+ public static void ParseServerData() {
+
+ String str;
+ int i;
+
+ Com.DPrintf("Serverdata packet received.\n");
+ //
+ // wipe the client_state_t struct
+ //
+ CL.ClearState();
+ cls.state = ca_connected;
+
+ // parse protocol version number
+ i = MSG.ReadLong(net_message);
+ cls.serverProtocol = i;
+
+ // BIG HACK to let demos from release work with the 3.0x patch!!!
+ if (Com.ServerState() != 0 && PROTOCOL_VERSION == 34) {
+ }
+ else if (i != PROTOCOL_VERSION)
+ Com.Error(ERR_DROP, "Server returned version " + i + ", not " + PROTOCOL_VERSION);
+
+ cl.servercount = MSG.ReadLong(net_message);
+ cl.attractloop = MSG.ReadByte(net_message) != 0;
+
+ // game directory
+ str = MSG.ReadString(net_message);
+ cl.gamedir = str;
+
+ // set gamedir
+ if (str.length() > 0
+ && (FS.fs_gamedirvar.string == null || FS.fs_gamedirvar.string.length() == 0 || FS.fs_gamedirvar.string.equals(str))
+ || (str.length() == 0 && (FS.fs_gamedirvar.string != null || FS.fs_gamedirvar.string.length() == 0)))
+ Cvar.Set("game", str);
+
+ // parse player entity number
+ cl.playernum = MSG.ReadShort(net_message);
+
+ // get the full level name
+ str = MSG.ReadString(net_message);
+
+ if (cl.playernum == -1) { // playing a cinematic or showing a pic, not a level
+ SCR.PlayCinematic(str);
+ }
+ else {
+ // seperate the printfs so the server message can have a color
+// Com.Printf(
+// "\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
+// Com.Printf('\02' + str + "\n");
+ Com.Printf("Levelname:" + str + "\n");
+ // need to prep refresh at next oportunity
+ cl.refresh_prepped = false;
+ }
+ }
+
+ /*
+ ==================
+ CL_ParseBaseline
+ ==================
+ */
+ public static void ParseBaseline() {
+ entity_state_t es;
+ int newnum;
+
+ entity_state_t nullstate = new entity_state_t(null);
+ //memset(nullstate, 0, sizeof(nullstate));
+ CM.intwrap bits = new CM.intwrap(0);
+ newnum = CL_ents.ParseEntityBits(bits);
+ es = cl_entities[newnum].baseline;
+ CL_ents.ParseDelta(nullstate, es, newnum, bits.i);
+ }
+
+ /*
+ ================
+ CL_LoadClientinfo
+
+ ================
+ */
+ public static void LoadClientinfo(clientinfo_t ci, String s) {
+ int i;
+ int t;
+
+ //char model_name[MAX_QPATH];
+ //char skin_name[MAX_QPATH];
+ //char model_filename[MAX_QPATH];
+ //char skin_filename[MAX_QPATH];
+ //char weapon_filename[MAX_QPATH];
+
+ String model_name,skin_name,model_filename, skin_filename, weapon_filename;
+
+ ci.cinfo = s;
+ //ci.cinfo[sizeof(ci.cinfo) - 1] = 0;
+
+ // isolate the player's name
+ ci.name = s;
+ //ci.name[sizeof(ci.name) - 1] = 0;
+
+ t = s.indexOf('\\');
+ //t = strstr(s, "\\");
+
+ if (t!=-1) {
+ ci.name = s.substring(0,t);
+ s = s.substring(t + 1, s.length());
+ //s = t + 1;
+ }
+
+ if (cl_noskins.value!=0 || s.length()!=0) {
+
+ model_filename=("players/male/tris.md2");
+ weapon_filename=("players/male/weapon.md2");
+ skin_filename=("players/male/grunt.pcx");
+ ci.iconname=("/players/male/grunt_i.pcx");
+
+ ci.model = re.RegisterModel(model_filename);
+
+ ci.weaponmodel = new model_t[Defines.MAX_CLIENTWEAPONMODELS];
+ ci.weaponmodel[0] = re.RegisterModel(weapon_filename);
+ ci.skin = re.RegisterSkin(skin_filename);
+ ci.icon = re.RegisterPic(ci.iconname);
+ }
+ else {
+ // isolate the model name
+
+ int pos = s.indexOf('/');
+
+ if (pos == -1)
+ pos = s.indexOf('/');
+ if (pos == -1)
+ {
+ pos =0;
+ Com.Error(Defines.ERR_FATAL, "Invalid model name:" + s);
+ }
+
+ model_name = s.substring(0,pos);
+
+ // isolate the skin name
+ skin_name = s.substring(pos+1, s.length());
+
+ // model file
+ model_filename = "players/" + model_name + "/tris.md2";
+ ci.model = re.RegisterModel(model_filename);
+
+ if (ci.model==null) {
+ model_name = "male";
+ model_filename= "players/male/tris.md2";
+ ci.model = re.RegisterModel(model_filename);
+ }
+
+ // skin file
+ skin_filename = "players/" + model_name +"/"+ skin_name + ".pcx";
+ ci.skin = re.RegisterSkin(skin_filename);
+
+ // if we don't have the skin and the model wasn't male,
+ // see if the male has it (this is for CTF's skins)
+ if (ci.skin==null && !model_name.equalsIgnoreCase("male")) {
+ // change model to male
+ model_name = "male";
+ model_filename= "players/male/tris.md2";
+ ci.model = re.RegisterModel(model_filename);
+
+ // see if the skin exists for the male model
+ skin_filename= "players/" + model_name + "/"+ skin_name+".pcx";
+ ci.skin = re.RegisterSkin(skin_filename);
+ }
+
+ // if we still don't have a skin, it means that the male model didn't have
+ // it, so default to grunt
+ if (ci.skin==null) {
+ // see if the skin exists for the male model
+ skin_filename= "players/" + model_name + "/grunt.pcx";
+ ci.skin = re.RegisterSkin(skin_filename);
+ }
+
+ // weapon file
+ for (i = 0; i < num_cl_weaponmodels; i++) {
+ weapon_filename= "players/"+model_name +"/" + cl_weaponmodels[i];
+ ci.weaponmodel[i] = re.RegisterModel(weapon_filename);
+ if (null==ci.weaponmodel[i] && model_name.equals("cyborg")) {
+ // try male
+ weapon_filename="players/male/" + cl_weaponmodels[i];
+ ci.weaponmodel[i] = re.RegisterModel(weapon_filename);
+ }
+ if (0==cl_vwep.value)
+ break; // only one when vwep is off
+ }
+
+ // icon file
+ ci.iconname= "/players/"+model_name+"/" + skin_name+"_i.pcx";
+ ci.icon = re.RegisterPic(ci.iconname);
+ }
+
+ // must have loaded all data types to be valud
+ if (ci.skin==null || ci.icon==null || ci.model==null || ci.weaponmodel[0]==null) {
+ ci.skin = null;
+ ci.icon = null;
+ ci.model = null;
+ ci.weaponmodel[0] = null;
+ return;
+ }
+ }
+
+ /*
+ ================
+ CL_ParseClientinfo
+
+ Load the skin, icon, and model for a client
+ ================
+ */
+ static void ParseClientinfo(int player) {
+ String s;
+ clientinfo_t ci;
+
+ s = cl.configstrings[player + CS_PLAYERSKINS];
+
+ ci = cl.clientinfo[player];
+
+ LoadClientinfo(ci, s);
+ }
+
+ /*
+ ================
+ CL_ParseConfigString
+ ================
+ */
+ public static void ParseConfigString() {
+ int i;
+ String s;
+ String olds;
+
+ i = MSG.ReadShort(net_message);
+
+ if (i < 0 || i >= MAX_CONFIGSTRINGS)
+ Com.Error(ERR_DROP, "configstring > MAX_CONFIGSTRINGS");
+
+ s = MSG.ReadString(net_message);
+
+ olds=cl.configstrings[i];
+ cl.configstrings[i] = s;
+
+ // do something apropriate
+
+ if (i >= CS_LIGHTS && i < CS_LIGHTS + MAX_LIGHTSTYLES)
+ {
+ SetLightstyle(i - CS_LIGHTS);
+ }
+ else if (i == CS_CDTRACK) {
+ if (cl.refresh_prepped)
+ CDAudio.CDAudio_Play(atoi(cl.configstrings[CS_CDTRACK]), true);
+ }
+ else if (i >= CS_MODELS && i < CS_MODELS + MAX_MODELS) {
+ if (cl.refresh_prepped) {
+ cl.model_draw[i - CS_MODELS] = re.RegisterModel(cl.configstrings[i]);
+ if (cl.configstrings[i].startsWith("*"))
+ cl.model_clip[i - CS_MODELS] = CM.InlineModel(cl.configstrings[i]);
+ else
+ cl.model_clip[i - CS_MODELS] = null;
+ }
+ }
+ else if (i >= CS_SOUNDS && i < CS_SOUNDS + MAX_MODELS) {
+ if (cl.refresh_prepped)
+ cl.sound_precache[i - CS_SOUNDS] = SND_DMA.RegisterSound(cl.configstrings[i]);
+ }
+ else if (i >= CS_IMAGES && i < CS_IMAGES + MAX_MODELS) {
+ if (cl.refresh_prepped)
+ cl.image_precache[i - CS_IMAGES] = re.RegisterPic(cl.configstrings[i]);
+ }
+ else if (i >= CS_PLAYERSKINS && i < CS_PLAYERSKINS + MAX_CLIENTS) {
+ if (cl.refresh_prepped && strcmp(olds, s)!=0)
+ ParseClientinfo(i - CS_PLAYERSKINS);
+ }
+ }
+
+ /*
+ =====================================================================
+
+ ACTION MESSAGES
+
+ =====================================================================
+ */
+
+ /*
+ ==================
+ CL_ParseStartSoundPacket
+ ==================
+ */
+ public static void ParseStartSoundPacket() {
+ float[] pos_v={0,0,0};
+ float pos[];
+ int channel, ent;
+ int sound_num;
+ float volume;
+ float attenuation;
+ int flags;
+ float ofs;
+
+ flags = MSG.ReadByte(net_message);
+ sound_num = MSG.ReadByte(net_message);
+
+ if ((flags & SND_VOLUME) != 0)
+ volume = MSG.ReadByte(net_message) / 255.0f;
+ else
+ volume = DEFAULT_SOUND_PACKET_VOLUME;
+
+ if ((flags & SND_ATTENUATION) != 0)
+ attenuation = MSG.ReadByte(net_message) / 64.0f;
+ else
+ attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
+
+ if ((flags & SND_OFFSET) != 0)
+ ofs = MSG.ReadByte(net_message) / 1000.0f;
+ else
+ ofs = 0;
+
+ if ((flags & SND_ENT) != 0) { // entity reletive
+ channel = MSG.ReadShort(net_message);
+ ent = channel >> 3;
+ if (ent > MAX_EDICTS)
+ Com.Error(ERR_DROP, "CL_ParseStartSoundPacket: ent = " + ent);
+
+ channel &= 7;
+ }
+ else {
+ ent = 0;
+ channel = 0;
+ }
+
+ if ((flags & SND_POS) != 0) { // positioned in space
+ MSG.ReadPos(net_message, pos_v);
+
+ pos = pos_v;
+ }
+ else // use entity number
+ pos = null;
+
+ if (null==cl.sound_precache[sound_num])
+ return;
+
+ SND_DMA.StartSound(pos, ent, channel, cl.sound_precache[sound_num], volume, attenuation, ofs);
+ }
+
+ public static void SHOWNET(String s) {
+ if (cl_shownet.value >= 2)
+ Com.Printf(net_message.readcount - 1 + ":" + s + "\n");
+ }
+
+ /*
+ =====================
+ CL_ParseServerMessage
+ =====================
+ */
+ public static void ParseServerMessage() {
+ int cmd;
+ String s;
+ int i;
+
+ //
+ // if recording demos, copy the message out
+ //
+ //if (cl_shownet.value == 1)
+ //Com.Printf(net_message.cursize + " ");
+ //else if (cl_shownet.value >= 2)
+ //Com.Printf("------------------\n");
+
+ //
+ // parse the message
+ //
+ while (true) {
+ if (net_message.readcount > net_message.cursize) {
+ Com.Error(ERR_FATAL, "CL_ParseServerMessage: Bad server message:");
+ break;
+ }
+
+ cmd = MSG.ReadByte(net_message);
+
+ if (cmd == -1) {
+ SHOWNET("END OF MESSAGE");
+ break;
+ }
+
+ if (cl_shownet.value >= 2) {
+ if (null == svc_strings[cmd])
+ Com.Printf(net_message.readcount - 1 + ":BAD CMD " + cmd + "\n");
+ else
+ SHOWNET(svc_strings[cmd]);
+ }
+
+ // other commands
+ switch (cmd) {
+ default :
+ Com.Error(ERR_DROP, "CL_ParseServerMessage: Illegible server message\n");
+ break;
+
+ case svc_nop :
+ // Com.Printf ("svc_nop\n");
+ break;
+
+ case svc_disconnect :
+ Com.Error(ERR_DISCONNECT, "Server disconnected\n");
+ break;
+
+ case svc_reconnect :
+ Com.Printf("Server disconnected, reconnecting\n");
+ if (cls.download != null) {
+ //ZOID, close download
+ fclose(cls.download);
+ cls.download = null;
+ }
+ cls.state = ca_connecting;
+ cls.connect_time = -99999; // CL_CheckForResend() will fire immediately
+ break;
+
+ case svc_print :
+ i = MSG.ReadByte(net_message);
+ if (i == PRINT_CHAT) {
+ SND_DMA.StartLocalSound("misc/talk.wav");
+ con.ormask = 128;
+ }
+ Com.Printf(MSG.ReadString(net_message));
+ con.ormask = 0;
+ break;
+
+ case svc_centerprint :
+ SCR.CenterPrint(MSG.ReadString(net_message));
+ break;
+
+ case svc_stufftext :
+ s = MSG.ReadString(net_message);
+ Com.DPrintf("stufftext: " + s + "\n");
+ Cbuf.AddText(s);
+ break;
+
+ case svc_serverdata :
+ Cbuf.Execute(); // make sure any stuffed commands are done
+ ParseServerData();
+ break;
+
+ case svc_configstring :
+ ParseConfigString();
+ break;
+
+ case svc_sound :
+ ParseStartSoundPacket();
+ break;
+
+ case svc_spawnbaseline :
+ ParseBaseline();
+ break;
+
+ case svc_temp_entity :
+ ParseTEnt();
+ break;
+
+ case svc_muzzleflash :
+ CL_fx.ParseMuzzleFlash();
+ break;
+
+ case svc_muzzleflash2 :
+ ParseMuzzleFlash2();
+ break;
+
+ case svc_download :
+ ParseDownload();
+ break;
+
+ case svc_frame :
+ ParseFrame();
+ break;
+
+ case svc_inventory :
+ CL_inv.ParseInventory();
+ break;
+
+ case svc_layout :
+ s = MSG.ReadString(net_message);
+ cl.layout = s;
+ break;
+
+ case svc_playerinfo :
+ case svc_packetentities :
+ case svc_deltapacketentities :
+ Com.Error(ERR_DROP, "Out of place frame data");
+ break;
+ }
+ }
+
+ CL_view.AddNetgraph();
+
+ //
+ // we don't know if it is ok to save a demo message until
+ // after we have parsed the frame
+ //
+ if (cls.demorecording && !cls.demowaiting)
+ CL.WriteDemoMessage();
+ }
+}