diff options
Diffstat (limited to 'src/jake2/server/SV_CCMDS.java')
-rw-r--r-- | src/jake2/server/SV_CCMDS.java | 1190 |
1 files changed, 1190 insertions, 0 deletions
diff --git a/src/jake2/server/SV_CCMDS.java b/src/jake2/server/SV_CCMDS.java new file mode 100644 index 0000000..baa1478 --- /dev/null +++ b/src/jake2/server/SV_CCMDS.java @@ -0,0 +1,1190 @@ +/* +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 18.01.2004 by RST. +// $Id: SV_CCMDS.java,v 1.1 2004-07-07 19:59:47 hzi Exp $ + +package jake2.server; + +import jake2.Globals; +import jake2.game.*; +import jake2.qcommon.*; +import jake2.sys.NET; +import jake2.sys.Sys; +import jake2.util.Lib; +import jake2.util.Vargs; + +import java.io.*; +import java.util.Date; + +public class SV_CCMDS extends SV_ENTS { + + /* + =============================================================================== + + OPERATOR CONSOLE ONLY COMMANDS + + These commands can only be entered from stdin or by a remote operator datagram + =============================================================================== + */ + + /* + ==================== + SV_SetMaster_f + + Specify a list of master servers + ==================== + */ + public static void SV_SetMaster_f() { + int i, slot; + + // only dedicated servers send heartbeats + if (dedicated.value == 0) { + Com.Printf("Only dedicated servers use masters.\n"); + return; + } + + // make sure the server is listed public + Cvar.Set("public", "1"); + + for (i = 1; i < MAX_MASTERS; i++) + //memset (&master_adr[i], 0, sizeof(master_adr[i])); + 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) + break; + + if (!NET.StringToAdr(Cmd.Argv(i), 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"); + + Com.Printf("Sending a ping.\n"); + + Netchan.OutOfBandPrint(NS_SERVER, master_adr[slot], "ping"); + + slot++; + } + + svs.last_heartbeat = -9999999; + } + + /* + ================== + SV_SetPlayer + + Sets sv_client and sv_player to the player with idnum Cmd.Argv(1) + ================== + */ + public static boolean SV_SetPlayer() { + client_t cl; + int i; + int idnum; + String s; + + if (Cmd.Argc() < 2) + return false; + + 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) { + Com.Printf("Bad client slot: " + idnum + "\n"); + return false; + } + + sv_client = svs.clients[idnum]; + sv_player = sv_client.edict; + if (0 == sv_client.state) { + Com.Printf("Client " + idnum + " is not active\n"); + return false; + } + return true; + } + + // check for a name match + for (i = 0; i < maxclients.value; i++) { + cl = svs.clients[i]; + if (0 == cl.state) + continue; + if (0 == strcmp(cl.name, s)) { + sv_client = cl; + sv_player = sv_client.edict; + return true; + } + } + + Com.Printf("Userid " + s + " is not on the server\n"); + return false; + } + + /* + =============================================================================== + + SAVEGAME FILES + + =============================================================================== + */ + + public static void remove(String name) { + try { + new File(name).delete(); + } + catch (Exception e) { + } + } + + /* + ===================== + SV_WipeSavegame + + Delete save/<XXX>/ + ===================== + */ + public static void SV_WipeSavegame(String savename) { + //char name[MAX_OSPATH]; + //char *s; + + String name, s; + + Com.DPrintf("SV_WipeSaveGame(" + savename + ")\n"); + + name = FS.Gamedir() + "/save/" + savename + "/server.ssv"; + remove(name); + + name = FS.Gamedir() + "/save/" + savename + "/game.ssv"; + remove(name); + + name = FS.Gamedir() + "/save/" + savename + "/*.sav"; + + File f = Sys.FindFirst(name, 0, 0); + while (f != null) { + f.delete(); + f = Sys.FindNext(); + } + Sys.FindClose(); + + name = FS.Gamedir() + "/save/" + savename + "/*.sv2"; + + f = Sys.FindFirst(name, 0, 0); + + while (f != null) { + f.delete(); + f = Sys.FindNext(); + } + Sys.FindClose(); + } + + /* + ================ + CopyFile + ================ + */ + public static void CopyFile(String src, String dst) { + RandomAccessFile f1, f2; + int l = -1; + byte buffer[] = new byte[65536]; + + Com.DPrintf("CopyFile (" + src + ", " + dst + ")\n"); + + try { + f1 = new RandomAccessFile(src, "r"); + } + catch (Exception e) { + return; + } + try { + + f2 = new RandomAccessFile(dst, "rw"); + } + catch (Exception e) { + try { + f1.close(); + } + catch (IOException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + return; + } + + while (true) { + + try { + l = f1.read(buffer, 0, 65536); + } + catch (IOException e1) { + + e1.printStackTrace(); + } + if (l == -1) + break; + try { + f2.write(buffer, 0, l); + } + catch (IOException e2) { + + e2.printStackTrace(); + } + } + + try { + f1.close(); + } + catch (IOException e1) { + + e1.printStackTrace(); + } + try { + f2.close(); + } + catch (IOException e2) { + + e2.printStackTrace(); + } + } + + /* + ================ + SV_CopySaveGame + ================ + */ + public static void SV_CopySaveGame(String src, String dst) { + //char name[MAX_OSPATH], name2[MAX_OSPATH]; + int l, len; + File found; + + String name, name2; + + Com.DPrintf("SV_CopySaveGame(" + src + "," + dst + ")\n"); + + SV_WipeSavegame(dst); + + // copy the savegame over + 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 = "/save/" + dst + "/game.ssv"; + CopyFile(name, name2); + + String name1 = FS.Gamedir() + "/save/" + src + "/"; + len = strlen(name1); + name = FS.Gamedir() + "/save/" + src + "/*.sav"; + + found = Sys.FindFirst(name, 0, 0); + + while (found != null) { + name = name1 + '/' + found.getName(); + name2 = FS.Gamedir() + "/save/" + dst + "/" + found.getName(); + CopyFile(name, name2); + + // change sav to sv2 + //l = strlen(name); + //strcpy(name + l - 3, "sv2"); + //l = strlen(name2); + //strcpy(name2 + l - 3, "sv2"); + name = name.substring(0, name.length() - 3) + "sv2"; + name2 = name.substring(0, name2.length() - 3) + "sv2"; + + CopyFile(name, name2); + + found = Sys.FindNext(); + } + Sys.FindClose(); + } + + /* + ============== + SV_WriteLevelFile + + ============== + */ + public static void SV_WriteLevelFile() { + //char name[MAX_OSPATH]; + //FILE * f; + + String name; + RandomAccessFile f; + + Com.DPrintf("SV_WriteLevelFile()\n"); + + name = FS.Gamedir() + "/save/current/" + sv.name + ".sv2"; + + try { + f = new RandomAccessFile(name, "rw"); + } + catch (Exception e) { + Com.Printf("Failed to open " + name + "\n"); + return; + } + try { + //fwrite(sv.configstrings, sizeof(sv.configstrings), 1, f); + for (int i = 0; i < sv.configstrings.length; i++) + Lib.fwriteString(sv.configstrings[i], MAX_QPATH, f); + + CM.CM_WritePortalState(f); + f.close(); + } + catch (Exception e) { + Com.Printf("IOError in SV_WriteLevelFile: " + e); + e.printStackTrace(); + } + + name = FS.Gamedir() + "/save/current/" + sv.name + ".sav"; + ge.WriteLevel(name); + } + + /* + ============== + SV_ReadLevelFile + + ============== + */ + public static void SV_ReadLevelFile() { + //char name[MAX_OSPATH]; + String name; + RandomAccessFile f; + + Com.DPrintf("SV_ReadLevelFile()\n"); + + name = FS.Gamedir() + "/save/current/" + sv.name + ".sv2"; + try { + f = new RandomAccessFile(name, "r"); + } + catch (Exception e) { + Com.Printf("Failed to open " + name + "\n"); + return; + } + // FS.Read(sv.configstrings, sizeof(sv.configstrings), f); + for (int n = 0; n < MAX_CONFIGSTRINGS; n++) + sv.configstrings[n] = Lib.freadString(f, MAX_QPATH); + + CM.CM_ReadPortalState(f); + + try { + f.close(); + } + catch (IOException e1) { + e1.printStackTrace(); + } + + name = FS.Gamedir() + "/save/current/" + sv.name + ".sav"; + ge.ReadLevel(name); + } + + /* + ============== + SV_WriteServerFile + + ============== + */ + public static void SV_WriteServerFile(boolean autosave) { + RandomAccessFile f; + cvar_t var; + //char name[MAX_OSPATH], string[128]; + //char comment[32]; + //time_t aclock; + //struct tm * newtime; + String name, string, comment; + + Com.DPrintf("SV_WriteServerFile(" + (autosave ? "true" : "false") + ")\n"); + + name = FS.Gamedir() + "/save/current/server.ssv"; + try { + f = new RandomAccessFile(name, "rw"); + } catch (FileNotFoundException e) { + f = null; + } + if (f == null) { + Com.Printf("Couldn't write " + name + "\n"); + return; + } + // write the comment field + //memset(comment, 0, sizeof(comment)); + + if (!autosave) { + //time( aclock); + //newtime = localtime( aclock); + Date newtime = new Date(); + comment = + Com.sprintf( + "%2i:%2i %2i/%2i ", + new Vargs().add(newtime.getHours()).add(newtime.getMinutes()).add(newtime.getMonth() + 1).add(newtime.getDay())); + comment += sv.configstrings[CS_NAME]; + } + else { // autosaved + comment = "ENTERING " + sv.configstrings[CS_NAME]; + } + + try { + fwriteString(comment, 32, f); + fwriteString(svs.mapcmd, MAX_TOKEN_CHARS, f); + + } catch (IOException e1) {} + + // 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)) + continue; + if (strlen(var.name) >= MAX_OSPATH - 1 || strlen(var.string) >= 128 - 1) { + Com.Printf("Cvar too long: " + var.name + " = " + var.string + "\n"); + continue; + } + //memset(name, 0, sizeof(name)); + //memset(string, 0, sizeof(string)); + name = var.name; + string = var.string; + try { + fwriteString(name, MAX_OSPATH, f); + fwriteString(string, 128, f); + } catch (IOException e2) {} + + } + + try { + f.close(); + } catch (IOException e2) {} + + // write game state + name = FS.Gamedir() + "/save/current/game.ssv"; + ge.WriteGame(name, autosave); + } + + /* + ============== + SV_ReadServerFile + + ============== + */ + public static void SV_ReadServerFile() { + RandomAccessFile f; + //char name[MAX_OSPATH], string[128]; + //char comment[32]; + //char mapcmd[MAX_TOKEN_CHARS]; + + String name, string, comment, mapcmd; + + Com.DPrintf("SV_ReadServerFile()\n"); + + name = FS.Gamedir() + "/save/current/server.ssv"; + try { + f = new RandomAccessFile(name, "r"); + } + catch (FileNotFoundException e1) { + Com.Printf("Couldn't read " + name + "\n"); + e1.printStackTrace(); + return; + } + // read the comment field + comment = Lib.freadString(f, 32); + + // read the mapcmd + mapcmd = Lib.freadString(f, MAX_TOKEN_CHARS); + + // read all CVAR_LATCH cvars + // these will be things like coop, skill, deathmatch, etc + while (true) { + name = Lib.freadString(f, MAX_OSPATH); + //if (!fread(name, 1, sizeof(name), f)) + if (name == null) + break; + string = Lib.freadString(f, 128); + Com.DPrintf("Set " + name + " = " + string + "\n"); + Cvar.ForceSet(name, string); + } + + try { + f.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + + // start a new game fresh with new cvars + SV_InitGame(); + + svs.mapcmd = mapcmd; + + // read game state + name = FS.Gamedir() + "/save/current/game.ssv"; + ge.ReadGame(name); + } + + //========================================================= + + /* + ================== + SV_DemoMap_f + + Puts the server in demo mode on a specific map/cinematic + ================== + */ + public static void SV_DemoMap_f() { + SV_Map(true, Cmd.Argv(1), false); + } + + /* + ================== + SV_GameMap_f + + Saves the state of the map just being exited and goes to a new map. + + If the initial character of the map string is '*', the next map is + in a new unit, so the current savegame directory is cleared of + map files. + + Example: + + *inter.cin+jail + + Clears the archived maps, plays the inter.cin cinematic, then + goes to map jail.bsp. + ================== + */ + public static void SV_GameMap_f() { + String map; + int i; + client_t cl; + boolean savedInuse[]; + + if (Cmd.Argc() != 2) { + Com.Printf("USAGE: gamemap <map>\n"); + return; + } + + Com.DPrintf("SV_GameMap(" + Cmd.Argv(1) + ")\n"); + + FS.CreatePath(FS.Gamedir() + "/save/current/"); + + // check for clearing the current savegame + 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) { + // 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; + } + + 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]; + + } + savedInuse = null; + } + } + + // start up the next map + SV_Map(false, Cmd.Argv(1), false); + + // archive server state + svs.mapcmd = Cmd.Argv(1); + + // copy off the level to the autosave slot + if (0 == dedicated.value) { + + //TODO: SV_WriteServerFile! + //SV_WriteServerFile(true); + + //SV_CopySaveGame("current", "save0"); + } + } + + /* + ================== + SV_Map_f + + Goes directly to a given map without any savegame archiving. + For development work + ================== + */ + public static void SV_Map_f() { + String map; + //char expanded[MAX_QPATH]; + String expanded; + + // if not a pcx, demo, or cinematic, check to make sure the level exists + map = Cmd.Argv(1); + if (!strstr(map, ".")) { + expanded = "maps/" + map + ".bsp"; + if (FS.LoadFile(expanded) == null) { + Com.Printf("Can't find " + expanded + "\n"); + return; + } + } + + sv.state = ss_dead; // don't save current level when changing + //TODO: RST: disabled for debugging + //SV_WipeSavegame("current"); + SV_GameMap_f(); + } + + /* + ===================================================================== + + SAVEGAMES + + ===================================================================== + */ + + /* + ============== + SV_Loadgame_f + + ============== + */ + public static void SV_Loadgame_f() { + //char name[MAX_OSPATH]; + //FILE * f; + //char * dir; + + String name; + RandomAccessFile f; + String dir; + + if (Cmd.Argc() != 2) { + Com.Printf("USAGE: loadgame <directory>\n"); + return; + } + + Com.Printf("Loading game...\n"); + + dir = Cmd.Argv(1); + if (strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\")) { + Com.Printf("Bad savedir.\n"); + } + + // make sure the server.ssv file exists + name = FS.Gamedir() + "/save/" + Cmd.Argv(1) + "/server.ssv"; + try { + f = new RandomAccessFile(name, "r"); + } + catch (FileNotFoundException e) { + Com.Printf("No such savegame: " + name + "\n"); + return; + } + + try { + f.close(); + } + catch (IOException e1) { + e1.printStackTrace(); + } + + SV_CopySaveGame(Cmd.Argv(1), "current"); + + 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_Savegame_f + + ============== + */ + public static void SV_Savegame_f() { + String dir; + + if (sv.state != ss_game) { + Com.Printf("You must be in a game to save.\n"); + return; + } + + if (Cmd.Argc() != 2) { + Com.Printf("USAGE: savegame <directory>\n"); + return; + } + + if (Cvar.VariableValue("deathmatch") != 0) { + Com.Printf("Can't savegame in a deathmatch\n"); + return; + } + + if (0 == 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) { + Com.Printf("\nCan't savegame while dead!\n"); + return; + } + + dir = Cmd.Argv(1); + if (strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\")) { + Com.Printf("Bad savedir.\n"); + } + + Com.Printf("Saving game...\n"); + + // archive current level, including all client edicts. + // when the level is reloaded, they will be shells awaiting + // a connecting client + SV_WriteLevelFile(); + + // save server state + try { + SV_WriteServerFile(false); + } + catch (Exception e) { + Com.Printf("IOError in SV_WriteServerFile: " + e); + } + + // copy it off + SV_CopySaveGame("current", dir); + + Com.Printf("Done.\n"); + } + + //=============================================================== + + /* + ================== + SV_Kick_f + + Kick a user off of the server + ================== + */ + public static void SV_Kick_f() { + if (!svs.initialized) { + Com.Printf("No server running.\n"); + return; + } + + if (Cmd.Argc() != 2) { + Com.Printf("Usage: kick <userid>\n"); + return; + } + + if (!SV_SetPlayer()) + return; + + SV_BroadcastPrintf(PRINT_HIGH, 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_Status_f + ================ + */ + public static void SV_Status_f() { + int i, j, l; + client_t cl; + String s; + int ping; + if (svs.clients == null) { + Com.Printf("No server running.\n"); + return; + } + Com.Printf("map : " + 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]; + 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])); + + if (cl.state == cs_connected) + Com.Printf("CNCT "); + else if (cl.state == cs_zombie) + Com.Printf("ZMBI "); + else { + ping = cl.ping < 9999 ? cl.ping : 9999; + Com.Printf("%4i ", new Vargs().add(ping)); + } + + Com.Printf("%s", new Vargs().add(cl.name)); + l = 16 - strlen(cl.name); + for (j = 0; j < l; j++) + Com.Printf(" "); + + Com.Printf("%7i ", new Vargs().add(svs.realtime - cl.lastmessage)); + + s = NET.AdrToString(cl.netchan.remote_address); + Com.Printf(s); + l = 22 - strlen(s); + for (j = 0; j < l; j++) + Com.Printf(" "); + + Com.Printf("%5i", new Vargs().add(cl.netchan.qport)); + + Com.Printf("\n"); + } + Com.Printf("\n"); + } + + /* + ================== + SV_ConSay_f + ================== + */ + public static void SV_ConSay_f() { + client_t client; + int j; + String p; + String text; // char[1024]; + + if (Cmd.Argc() < 2) + return; + + text = "console: "; + p = Cmd.Args(); + + if (p.charAt(0) == '"') { + 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) + continue; + SV_ClientPrintf(client, PRINT_CHAT, text + "\n"); + } + } + + /* + ================== + SV_Heartbeat_f + ================== + */ + public static void SV_Heartbeat_f() { + svs.last_heartbeat = -9999999; + } + + /* + =========== + SV_Serverinfo_f + + Examine or change the serverinfo string + =========== + */ + public static void SV_Serverinfo_f() { + Com.Printf("Server info settings:\n"); + Info.Print(Cvar.Serverinfo()); + } + + /* + =========== + SV_DumpUser_f + + Examine all a users info strings + =========== + */ + public static void SV_DumpUser_f() { + if (Cmd.Argc() != 2) { + Com.Printf("Usage: info <userid>\n"); + return; + } + + if (!SV_SetPlayer()) + return; + + Com.Printf("userinfo\n"); + Com.Printf("--------\n"); + Info.Print(sv_client.userinfo); + + } + + /* + ============== + SV_ServerRecord_f + + Begins server demo recording. Every entity and every message will be + recorded, but no playerinfo will be stored. Primarily for demo merging. + ============== + */ + public static void SV_ServerRecord_f() { + //char name[MAX_OSPATH]; + String name; + byte buf_data[] = new byte[32768]; + sizebuf_t buf = new sizebuf_t(); + int len; + int i; + + if (Cmd.Argc() != 2) { + Com.Printf("serverrecord <demoname>\n"); + return; + } + + if (svs.demofile != null) { + Com.Printf("Already recording.\n"); + return; + } + + if (sv.state != ss_game) { + Com.Printf("You must be in a level to record.\n"); + return; + } + + // + // open the demo file + // + name = FS.Gamedir() + "/demos/" + Cmd.Argv(1) + ".dm2"; + + Com.Printf("recording to " + name + ".\n"); + FS.CreatePath(name); + try { + svs.demofile = new RandomAccessFile(name, "rw"); + } + catch (Exception e) { + Com.Printf("ERROR: couldn't open.\n"); + return; + } + + // setup a buffer to catch all multicasts + SZ.Init(svs.demo_multicast, svs.demo_multicast_buf, svs.demo_multicast_buf.length); + + // + // write a single giant fake message with all the startup info + // + SZ.Init(buf, buf_data, buf_data.length); + + // + // serverdata needs to go over for all types of servers + // 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); + // 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]); + + for (i = 0; i < MAX_CONFIGSTRINGS; i++) + if (sv.configstrings[i].length() == 0) { + MSG.WriteByte(buf, svc_configstring); + MSG.WriteShort(buf, i); + MSG.WriteString(buf, sv.configstrings[i]); + } + + // write it to the demo file + Com.DPrintf("signon message length: " + buf.cursize + "\n"); + 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); + } + catch (IOException e1) { + // TODO: do quake2 error handling! + e1.printStackTrace(); + } + + // the rest of the demo file will be individual frames + } + + /* + ============== + SV_ServerStop_f + + Ends server demo recording + ============== + */ + public static void SV_ServerStop_f() { + if (svs.demofile == null) { + Com.Printf("Not doing a serverrecord.\n"); + return; + } + try { + svs.demofile.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + svs.demofile = null; + Com.Printf("Recording completed.\n"); + } + + /* + =============== + SV_KillServer_f + + Kick everyone off, possibly in preparation for a new game + + =============== + */ + public static void SV_KillServer_f() { + if (!svs.initialized) + return; + SV_Shutdown("Server was killed.\n", false); + NET.Config(false); // close network sockets + } + + /* + =============== + SV_ServerCommand_f + + Let the game dll handle a command + =============== + */ + public static void SV_ServerCommand_f() { + if (SV_GAME.ge == null) { + Com.Printf("No game loaded.\n"); + return; + } + + SV_GAME.ge.ServerCommand(); + } + + //=========================================================== + + /* + ================== + SV_InitOperatorCommands + ================== + */ + public static void SV_InitOperatorCommands() { + Cmd.AddCommand("heartbeat", new xcommand_t() { + public void execute() { + SV_Heartbeat_f(); + } + }); + Cmd.AddCommand("kick", new xcommand_t() { + public void execute() { + SV_Kick_f(); + } + }); + Cmd.AddCommand("status", new xcommand_t() { + public void execute() { + SV_Status_f(); + } + }); + Cmd.AddCommand("serverinfo", new xcommand_t() { + public void execute() { + SV_Serverinfo_f(); + } + }); + Cmd.AddCommand("dumpuser", new xcommand_t() { + public void execute() { + SV_DumpUser_f(); + } + }); + + Cmd.AddCommand("map", new xcommand_t() { + public void execute() { + SV_Map_f(); + } + }); + Cmd.AddCommand("demomap", new xcommand_t() { + public void execute() { + SV_DemoMap_f(); + } + }); + Cmd.AddCommand("gamemap", new xcommand_t() { + public void execute() { + SV_GameMap_f(); + } + }); + Cmd.AddCommand("setmaster", new xcommand_t() { + public void execute() { + SV_SetMaster_f(); + } + }); + + if (dedicated.value != 0) + Cmd.AddCommand("say", new xcommand_t() { + public void execute() { + SV_ConSay_f(); + } + }); + + Cmd.AddCommand("serverrecord", new xcommand_t() { + public void execute() { + SV_ServerRecord_f(); + } + }); + Cmd.AddCommand("serverstop", new xcommand_t() { + public void execute() { + SV_ServerStop_f(); + } + }); + + Cmd.AddCommand("save", new xcommand_t() { + public void execute() { + SV_Savegame_f(); + } + }); + Cmd.AddCommand("load", new xcommand_t() { + public void execute() { + SV_Loadgame_f(); + } + }); + + Cmd.AddCommand("killserver", new xcommand_t() { + public void execute() { + SV_KillServer_f(); + } + }); + + Cmd.AddCommand("sv", new xcommand_t() { + public void execute() { + SV_ServerCommand_f(); + } + }); + } + +} |