// NET_Port_f static void Port_f() { if (Cmd.Argc != 2) { Con.Print("\"port\" is \"{0}\"\n", HostPort); return; } int n = Common.atoi(Cmd.Argv(1)); if (n < 1 || n > 65534) { Con.Print("Bad value, must be between 1 and 65534\n"); return; } _DefHostPort = n; HostPort = n; if (_IsListening) { // force a change to the new port Cbuf.AddText("listen 0\n"); Cbuf.AddText("listen 1\n"); } }
// Host_Startdemos_f static void Startdemos_f() { if (Client.cls.state == cactive_t.ca_dedicated) { if (!Server.sv.active) { Cbuf.AddText("map start\n"); } return; } int c = Cmd.Argc - 1; if (c > Client.MAX_DEMOS) { Con.Print("Max {0} demos in demoloop\n", Client.MAX_DEMOS); c = Client.MAX_DEMOS; } Con.Print("{0} demo(s) in loop\n", c); for (int i = 1; i < c + 1; i++) { Client.cls.demos[i - 1] = Common.Copy(Cmd.Argv(i), Client.MAX_DEMONAME); } if (!Server.sv.active && Client.cls.demonum != -1 && !Client.cls.demoplayback) { Client.cls.demonum = 0; Client.NextDemo(); } else { Client.cls.demonum = -1; } }
public static void Init(quakeparms_t parms) { _Params = parms; Cache.Init(1024 * 1024 * 16); // debug Cbuf.Init(); Cmd.Init(); View.Init(); Chase.Init(); InitVCR(parms); Common.Init(parms.basedir, parms.argv); InitLocal(); Wad.LoadWadFile("gfx.wad"); Key.Init(); Con.Init(); Menu.Init(); Progs.Init(); Mod.Init(); Net.Init(); Server.Init(); //Con.Print("Exe: "__TIME__" "__DATE__"\n"); //Con.Print("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0)); Render.InitTextures(); // needed even for dedicated servers if (Client.cls.state != cactive_t.ca_dedicated) { _BasePal = Common.LoadFile("gfx/palette.lmp"); if (_BasePal == null) { Sys.Error("Couldn't load gfx/palette.lmp"); } _ColorMap = Common.LoadFile("gfx/colormap.lmp"); if (_ColorMap == null) { Sys.Error("Couldn't load gfx/colormap.lmp"); } // on non win32, mouse comes before video for security reasons Input.Init(); Vid.Init(_BasePal); Drawer.Init(); Scr.Init(); Render.Init(); Sound.Init(); CDAudio.Init(); Sbar.Init(); Client.Init(); } Cbuf.InsertText("exec quake.rc\n"); _IsInitialized = true; Con.DPrint("========Quake Initialized=========\n"); }
// Host_GetConsoleCommands // // Add them exactly as if they had been typed at the console static void GetConsoleCommands() { while (true) { string cmd = Sys.ConsoleInput(); if (String.IsNullOrEmpty(cmd)) { break; } Cbuf.AddText(cmd); } }
/// <summary> /// Cmd_StuffCmds_f /// Adds command line parameters as script statements /// Commands lead with a +, and continue until a - or another + /// quake +prog jctest.qp +cmd amlev1 /// quake -nosound +cmd amlev1 /// </summary> static void StuffCmds_f() { if (_Argc != 1) { Con.Print("stuffcmds : execute command line parameters\n"); return; } // build the combined string to parse from StringBuilder sb = new StringBuilder(1024); for (int i = 1; i < _Argc; i++) { if (!String.IsNullOrEmpty(_Argv[i])) { sb.Append(_Argv[i]); if (i + 1 < _Argc) { sb.Append(" "); } } } // pull out the commands string text = sb.ToString(); sb.Length = 0; for (int i = 0; i < text.Length; i++) { if (text[i] == '+') { i++; int j = i; while ((j < text.Length) && (text[j] != '+') && (text[j] != '-')) { j++; } sb.Append(text.Substring(i, j - i + 1)); sb.AppendLine(); i = j - 1; } } if (sb.Length > 0) { Cbuf.InsertText(sb.ToString()); } }
/* * ============== * PF_changelevel * ============== */ static void PF_changelevel() { // make sure we don't issue two changelevels if (Server.svs.changelevel_issued) { return; } Server.svs.changelevel_issued = true; string s = GetString(OFS.OFS_PARM0); Cbuf.AddText(String.Format("changelevel {0}\n", s)); }
// MaxPlayers_f static void MaxPlayers_f() { if (Cmd.Argc != 2) { Con.Print("\"maxplayers\" is \"%u\"\n", Server.svs.maxclients); return; } if (Server.sv.active) { Con.Print("maxplayers can not be changed while a server is running.\n"); return; } int n = Common.atoi(Cmd.Argv(1)); if (n < 1) { n = 1; } if (n > Server.svs.maxclientslimit) { n = Server.svs.maxclientslimit; Con.Print("\"maxplayers\" set to \"{0}\"\n", n); } if (n == 1 && _IsListening) { Cbuf.AddText("listen 0\n"); } if (n > 1 && !_IsListening) { Cbuf.AddText("listen 1\n"); } Server.svs.maxclients = n; if (n == 1) { Cvar.Set("deathmatch", "0"); } else { Cvar.Set("deathmatch", "1"); } }
// Key_Message (int key) static void KeyMessage(int key) { if (key == K_ENTER) { if (_TeamMessage) { Cbuf.AddText("say_team \""); } else { Cbuf.AddText("say \""); } Cbuf.AddText(_ChatBuffer.ToString()); Cbuf.AddText("\"\n"); Key.Destination = keydest_t.key_game; _ChatBuffer.Length = 0; return; } if (key == K_ESCAPE) { Key.Destination = keydest_t.key_game; _ChatBuffer.Length = 0; return; } if (key < 32 || key > 127) { return; // non printable } if (key == K_BACKSPACE) { if (_ChatBuffer.Length > 0) { _ChatBuffer.Length--; } return; } if (_ChatBuffer.Length == 31) { return; // all full } _ChatBuffer.Append((char)key); }
// Cmd_Exec_f static void Exec_f() { if (_Argc != 2) { Con.Print("exec <filename> : execute a script file\n"); return; } byte[] bytes = Common.LoadFile(_Argv[1]); if (bytes == null) { Con.Print("couldn't exec {0}\n", _Argv[1]); return; } string script = Encoding.ASCII.GetString(bytes); Con.Print("execing {0}\n", _Argv[1]); Cbuf.InsertText(script); }
/// <summary> /// CL_NextDemo /// /// Called to play the next demo in the demo loop /// </summary> public static void NextDemo() { if (cls.demonum == -1) { return; // don't play demos } Scr.BeginLoadingPlaque(); if (String.IsNullOrEmpty(cls.demos[cls.demonum]) || cls.demonum == MAX_DEMOS) { cls.demonum = 0; if (String.IsNullOrEmpty(cls.demos[cls.demonum])) { Con.Print("No demos listed with startdemos\n"); cls.demonum = -1; return; } } Cbuf.InsertText(String.Format("playdemo {0}\n", cls.demos[cls.demonum])); cls.demonum++; }
// void Cmd_ExecuteString (char *text, cmd_source_t src); // Parses a single line of text into arguments and tries to execute it. // The text can come from the command buffer, a remote client, or stdin. // // A complete command line has been parsed, so try to execute it // FIXME: lookupnoadd the token to speed search? public static void ExecuteString(string text, cmd_source_t src) { _Source = src; TokenizeString(text); // execute the command line if (_Argc <= 0) { return; // no tokens } // check functions xcommand_t handler = Find(_Argv[0]); // must search with comparison like Q_strcasecmp() if (handler != null) { handler(); } else { // check alias string alias = FindAlias(_Argv[0]); // must search with compare func like Q_strcasecmp if (!String.IsNullOrEmpty(alias)) { Cbuf.InsertText(alias); } else { // check cvars if (!Cvar.Command()) { Con.Print("Unknown command \"{0}\"\n", _Argv[0]); } } } }
/// <summary> /// PF_localcmd /// Sends text over to the client's execution buffer /// localcmd (string) /// </summary> static void PF_localcmd() { string cmd = GetString(OFS.OFS_PARM0); Cbuf.AddText(cmd); }
/// <summary> /// Key_Console /// Interactive line editing and console scrollback /// </summary> static void KeyConsole(int key) { if (key == K_ENTER) { string line = new String(_Lines[_EditLine]).TrimEnd('\0', ' '); string cmd = line.Substring(1); Cbuf.AddText(cmd); // skip the > Cbuf.AddText("\n"); Con.Print("{0}\n", line); _EditLine = (_EditLine + 1) & 31; _HistoryLine = _EditLine; _Lines[_EditLine][0] = ']'; Key.LinePos = 1; if (Client.cls.state == cactive_t.ca_disconnected) { Scr.UpdateScreen(); // force an update, because the command } // may take some time return; } if (key == K_TAB) { // command completion string txt = new String(_Lines[_EditLine], 1, MAXCMDLINE - 1).TrimEnd('\0', ' '); string[] cmds = Cmd.Complete(txt); string[] vars = Cvar.CompleteName(txt); string match = null; if (cmds != null) { if (cmds.Length > 1 || vars != null) { Con.Print("\nCommands:\n"); foreach (string s in cmds) { Con.Print(" {0}\n", s); } } else { match = cmds[0]; } } if (vars != null) { if (vars.Length > 1 || cmds != null) { Con.Print("\nVariables:\n"); foreach (string s in vars) { Con.Print(" {0}\n", s); } } else if (match == null) { match = vars[0]; } } if (!String.IsNullOrEmpty(match)) { int len = Math.Min(match.Length, MAXCMDLINE - 3); for (int i = 0; i < len; i++) { _Lines[_EditLine][i + 1] = match[i]; } Key.LinePos = len + 1; _Lines[_EditLine][Key.LinePos] = ' '; Key.LinePos++; _Lines[_EditLine][Key.LinePos] = '\0'; return; } } if (key == K_BACKSPACE || key == K_LEFTARROW) { if (Key.LinePos > 1) { Key.LinePos--; } return; } if (key == K_UPARROW) { do { _HistoryLine = (_HistoryLine - 1) & 31; } while (_HistoryLine != _EditLine && (_Lines[_HistoryLine][1] == 0)); if (_HistoryLine == _EditLine) { _HistoryLine = (_EditLine + 1) & 31; } Array.Copy(_Lines[_HistoryLine], _Lines[_EditLine], MAXCMDLINE); Key.LinePos = 0; while (_Lines[_EditLine][Key.LinePos] != '\0' && Key.LinePos < MAXCMDLINE) { Key.LinePos++; } return; } if (key == K_DOWNARROW) { if (_HistoryLine == _EditLine) { return; } do { _HistoryLine = (_HistoryLine + 1) & 31; }while (_HistoryLine != _EditLine && (_Lines[_HistoryLine][1] == '\0')); if (_HistoryLine == _EditLine) { _Lines[_EditLine][0] = ']'; Key.LinePos = 1; } else { Array.Copy(_Lines[_HistoryLine], _Lines[_EditLine], MAXCMDLINE); Key.LinePos = 0; while (_Lines[_EditLine][Key.LinePos] != '\0' && Key.LinePos < MAXCMDLINE) { Key.LinePos++; } } return; } if (key == K_PGUP || key == K_MWHEELUP) { Con.BackScroll += 2; if (Con.BackScroll > Con.TotalLines - (Scr.vid.height >> 3) - 1) { Con.BackScroll = Con.TotalLines - (Scr.vid.height >> 3) - 1; } return; } if (key == K_PGDN || key == K_MWHEELDOWN) { Con.BackScroll -= 2; if (Con.BackScroll < 0) { Con.BackScroll = 0; } return; } if (key == K_HOME) { Con.BackScroll = Con.TotalLines - (Scr.vid.height >> 3) - 1; return; } if (key == K_END) { Con.BackScroll = 0; return; } if (key < 32 || key > 127) { return; // non printable } if (Key.LinePos < MAXCMDLINE - 1) { _Lines[_EditLine][Key.LinePos] = (char)key; Key.LinePos++; _Lines[_EditLine][Key.LinePos] = '\0'; } }
static float _LastMsg; // static float lastmsg from CL_KeepaliveMessage /// <summary> /// CL_ParseServerMessage /// </summary> static void ParseServerMessage() { // // if recording demos, copy the message out // if (_ShowNet.Value == 1) { Con.Print("{0} ", Net.Message.Length); } else if (_ShowNet.Value == 2) { Con.Print("------------------\n"); } cl.onground = false; // unless the server says otherwise // // parse the message // Net.Reader.Reset(); int i; while (true) { if (Net.Reader.IsBadRead) { Host.Error("CL_ParseServerMessage: Bad server message"); } int cmd = Net.Reader.ReadByte(); if (cmd == -1) { ShowNet("END OF MESSAGE"); return; // end of message } // if the high bit of the command byte is set, it is a fast update if ((cmd & 128) != 0) { ShowNet("fast update"); ParseUpdate(cmd & 127); continue; } ShowNet(_SvcStrings[cmd]); // other commands switch (cmd) { default: Host.Error("CL_ParseServerMessage: Illegible server message\n"); break; case Protocol.svc_nop: break; case Protocol.svc_time: cl.mtime[1] = cl.mtime[0]; cl.mtime[0] = Net.Reader.ReadFloat(); break; case Protocol.svc_clientdata: i = Net.Reader.ReadShort(); ParseClientData(i); break; case Protocol.svc_version: i = Net.Reader.ReadLong(); if (i != Protocol.PROTOCOL_VERSION) { Host.Error("CL_ParseServerMessage: Server is protocol {0} instead of {1}\n", i, Protocol.PROTOCOL_VERSION); } break; case Protocol.svc_disconnect: Host.EndGame("Server disconnected\n"); break; case Protocol.svc_print: Con.Print(Net.Reader.ReadString()); break; case Protocol.svc_centerprint: Scr.CenterPrint(Net.Reader.ReadString()); break; case Protocol.svc_stufftext: Cbuf.AddText(Net.Reader.ReadString()); break; case Protocol.svc_damage: View.ParseDamage(); break; case Protocol.svc_serverinfo: ParseServerInfo(); Scr.vid.recalc_refdef = true; // leave intermission full screen break; case Protocol.svc_setangle: cl.viewangles.X = Net.Reader.ReadAngle(); cl.viewangles.Y = Net.Reader.ReadAngle(); cl.viewangles.Z = Net.Reader.ReadAngle(); break; case Protocol.svc_setview: cl.viewentity = Net.Reader.ReadShort(); break; case Protocol.svc_lightstyle: i = Net.Reader.ReadByte(); if (i >= QDef.MAX_LIGHTSTYLES) { Sys.Error("svc_lightstyle > MAX_LIGHTSTYLES"); } _LightStyle[i].map = Net.Reader.ReadString(); break; case Protocol.svc_sound: ParseStartSoundPacket(); break; case Protocol.svc_stopsound: i = Net.Reader.ReadShort(); Sound.StopSound(i >> 3, i & 7); break; case Protocol.svc_updatename: Sbar.Changed(); i = Net.Reader.ReadByte(); if (i >= cl.maxclients) { Host.Error("CL_ParseServerMessage: svc_updatename > MAX_SCOREBOARD"); } cl.scores[i].name = Net.Reader.ReadString(); break; case Protocol.svc_updatefrags: Sbar.Changed(); i = Net.Reader.ReadByte(); if (i >= cl.maxclients) { Host.Error("CL_ParseServerMessage: svc_updatefrags > MAX_SCOREBOARD"); } cl.scores[i].frags = Net.Reader.ReadShort(); break; case Protocol.svc_updatecolors: Sbar.Changed(); i = Net.Reader.ReadByte(); if (i >= cl.maxclients) { Host.Error("CL_ParseServerMessage: svc_updatecolors > MAX_SCOREBOARD"); } cl.scores[i].colors = Net.Reader.ReadByte(); NewTranslation(i); break; case Protocol.svc_particle: Render.ParseParticleEffect(); break; case Protocol.svc_spawnbaseline: i = Net.Reader.ReadShort(); // must use CL_EntityNum() to force cl.num_entities up ParseBaseline(EntityNum(i)); break; case Protocol.svc_spawnstatic: ParseStatic(); break; case Protocol.svc_temp_entity: ParseTempEntity(); break; case Protocol.svc_setpause: { cl.paused = Net.Reader.ReadByte() != 0; if (cl.paused) { CDAudio.Pause(); } else { CDAudio.Resume(); } } break; case Protocol.svc_signonnum: i = Net.Reader.ReadByte(); if (i <= cls.signon) { Host.Error("Received signon {0} when at {1}", i, cls.signon); } cls.signon = i; SignonReply(); break; case Protocol.svc_killedmonster: cl.stats[QStats.STAT_MONSTERS]++; break; case Protocol.svc_foundsecret: cl.stats[QStats.STAT_SECRETS]++; break; case Protocol.svc_updatestat: i = Net.Reader.ReadByte(); if (i < 0 || i >= QStats.MAX_CL_STATS) { Sys.Error("svc_updatestat: {0} is invalid", i); } cl.stats[i] = Net.Reader.ReadLong(); break; case Protocol.svc_spawnstaticsound: ParseStaticSound(); break; case Protocol.svc_cdtrack: cl.cdtrack = Net.Reader.ReadByte(); cl.looptrack = Net.Reader.ReadByte(); if ((cls.demoplayback || cls.demorecording) && (cls.forcetrack != -1)) { CDAudio.Play((byte)cls.forcetrack, true); } else { CDAudio.Play((byte)cl.cdtrack, true); } break; case Protocol.svc_intermission: cl.intermission = 1; cl.completed_time = (int)cl.time; Scr.vid.recalc_refdef = true; // go to full screen break; case Protocol.svc_finale: cl.intermission = 2; cl.completed_time = (int)cl.time; Scr.vid.recalc_refdef = true; // go to full screen Scr.CenterPrint(Net.Reader.ReadString()); break; case Protocol.svc_cutscene: cl.intermission = 3; cl.completed_time = (int)cl.time; Scr.vid.recalc_refdef = true; // go to full screen Scr.CenterPrint(Net.Reader.ReadString()); break; case Protocol.svc_sellscreen: Cmd.ExecuteString("help", cmd_source_t.src_command); break; } } }
/// <summary> /// SV_ReadClientMessage /// Returns false if the client should be killed /// </summary> static bool ReadClientMessage() { while (true) { int ret = Net.GetMessage(Host.HostClient.netconnection); if (ret == -1) { Con.DPrint("SV_ReadClientMessage: NET_GetMessage failed\n"); return(false); } if (ret == 0) { return(true); } Net.Reader.Reset(); bool flag = true; while (flag) { if (!Host.HostClient.active) { return(false); // a command caused an error } if (Net.Reader.IsBadRead) { Con.DPrint("SV_ReadClientMessage: badread\n"); return(false); } int cmd = Net.Reader.ReadChar(); switch (cmd) { case -1: flag = false; // end of message ret = 1; break; case Protocol.clc_nop: break; case Protocol.clc_stringcmd: string s = Net.Reader.ReadString(); if (Host.HostClient.privileged) { ret = 2; } else { ret = 0; } if (Common.SameText(s, "status", 6)) { ret = 1; } else if (Common.SameText(s, "god", 3)) { ret = 1; } else if (Common.SameText(s, "notarget", 8)) { ret = 1; } else if (Common.SameText(s, "fly", 3)) { ret = 1; } else if (Common.SameText(s, "name", 4)) { ret = 1; } else if (Common.SameText(s, "noclip", 6)) { ret = 1; } else if (Common.SameText(s, "say", 3)) { ret = 1; } else if (Common.SameText(s, "say_team", 8)) { ret = 1; } else if (Common.SameText(s, "tell", 4)) { ret = 1; } else if (Common.SameText(s, "color", 5)) { ret = 1; } else if (Common.SameText(s, "kill", 4)) { ret = 1; } else if (Common.SameText(s, "pause", 5)) { ret = 1; } else if (Common.SameText(s, "spawn", 5)) { ret = 1; } else if (Common.SameText(s, "begin", 5)) { ret = 1; } else if (Common.SameText(s, "prespawn", 8)) { ret = 1; } else if (Common.SameText(s, "kick", 4)) { ret = 1; } else if (Common.SameText(s, "ping", 4)) { ret = 1; } else if (Common.SameText(s, "give", 4)) { ret = 1; } else if (Common.SameText(s, "ban", 3)) { ret = 1; } if (ret == 2) { Cbuf.InsertText(s); } else if (ret == 1) { Cmd.ExecuteString(s, cmd_source_t.src_client); } else { Con.DPrint("{0} tried to {1}\n", Host.HostClient.name, s); } break; case Protocol.clc_disconnect: return(false); case Protocol.clc_move: ReadClientMove(ref Host.HostClient.cmd); break; default: Con.DPrint("SV_ReadClientMessage: unknown command char\n"); return(false); } } if (ret != 1) { break; } } return(true); }
// _Host_Frame // //Runs all active servers static void InternalFrame(double time) { // keep the random time dependent Sys.Random(); // decide the simulation time if (!FilterTime(time)) { return; // don't run too fast, or packets will flood out } // get new key events Sys.SendKeyEvents(); // allow mice or other external controllers to add commands Input.Commands(); // process console commands Cbuf.Execute(); Net.Poll(); // if running the server locally, make intentions now if (Server.sv.active) { Client.SendCmd(); } //------------------- // // server operations // //------------------- // check for commands typed to the host GetConsoleCommands(); if (Server.sv.active) { ServerFrame(); } //------------------- // // client operations // //------------------- // if running the server remotely, send intentions now after // the incoming messages have been read if (!Server.sv.active) { Client.SendCmd(); } _Time += FrameTime; // fetch results from server if (Client.cls.state == cactive_t.ca_connected) { Client.ReadFromServer(); } // update video if (_Speeds.Value != 0) { _Time1 = Sys.GetFloatTime(); } Scr.UpdateScreen(); if (_Speeds.Value != 0) { _Time2 = Sys.GetFloatTime(); } // update audio if (Client.cls.signon == Client.SIGNONS) { Sound.Update(ref Render.Origin, ref Render.ViewPn, ref Render.ViewRight, ref Render.ViewUp); Client.DecayLights(); } else { Sound.Update(ref Common.ZeroVector, ref Common.ZeroVector, ref Common.ZeroVector, ref Common.ZeroVector); } CDAudio.Update(); if (_Speeds.Value != 0) { int pass1 = (int)((_Time1 - _Time3) * 1000); _Time3 = Sys.GetFloatTime(); int pass2 = (int)((_Time2 - _Time1) * 1000); int pass3 = (int)((_Time3 - _Time2) * 1000); Con.Print("{0,3} tot {1,3} server {2,3} gfx {3,3} snd\n", pass1 + pass2 + pass3, pass1, pass2, pass3); } _FrameCount++; }
// Key_Event (int key, qboolean down) // // Called by the system between frames for both key up and key down events // Should NOT be called during an interrupt! public static void Event(int key, bool down) { _KeyDown[key] = down; if (!down) { _Repeats[key] = 0; } _LastPress = key; KeyCount++; if (KeyCount <= 0) { return; // just catching keys for Con_NotifyBox } // update auto-repeat status if (down) { _Repeats[key]++; if (key != K_BACKSPACE && key != K_PAUSE && key != K_PGUP && key != K_PGDN && _Repeats[key] > 1) { return; // ignore most autorepeats } if (key >= 200 && String.IsNullOrEmpty(_Bindings[key])) { Con.Print("{0} is unbound, hit F4 to set.\n", KeynumToString(key)); } } if (key == K_SHIFT) { _ShiftDown = down; } // // handle escape specialy, so the user can never unbind it // if (key == K_ESCAPE) { if (!down) { return; } switch (_KeyDest) { case keydest_t.key_message: KeyMessage(key); break; case keydest_t.key_menu: Menu.KeyDown(key); break; case keydest_t.key_game: case keydest_t.key_console: Menu.ToggleMenu_f(); break; default: Sys.Error("Bad key_dest"); break; } return; } // // key up events only generate commands if the game key binding is // a button command (leading + sign). These will occur even in console mode, // to keep the character from continuing an action started before a console // switch. Button commands include the keynum as a parameter, so multiple // downs can be matched with ups // if (!down) { string kb = _Bindings[key]; if (!String.IsNullOrEmpty(kb) && kb.StartsWith("+")) { Cbuf.AddText(String.Format("-{0} {1}\n", kb.Substring(1), key)); } if (_KeyShift[key] != key) { kb = _Bindings[_KeyShift[key]]; if (!String.IsNullOrEmpty(kb) && kb.StartsWith("+")) { Cbuf.AddText(String.Format("-{0} {1}\n", kb.Substring(1), key)); } } return; } // // during demo playback, most keys bring up the main menu // if (Client.cls.demoplayback && down && _ConsoleKeys[key] && _KeyDest == keydest_t.key_game) { Menu.ToggleMenu_f(); return; } // // if not a consolekey, send to the interpreter no matter what mode is // if ((_KeyDest == keydest_t.key_menu && _MenuBound[key]) || (_KeyDest == keydest_t.key_console && !_ConsoleKeys[key]) || (_KeyDest == keydest_t.key_game && (!Con.ForcedUp || !_ConsoleKeys[key]))) { string kb = _Bindings[key]; if (!String.IsNullOrEmpty(kb)) { if (kb.StartsWith("+")) { // button commands add keynum as a parm Cbuf.AddText(String.Format("{0} {1}\n", kb, key)); } else { Cbuf.AddText(kb); Cbuf.AddText("\n"); } } return; } if (!down) { return; // other systems only care about key down events } if (_ShiftDown) { key = _KeyShift[key]; } switch (_KeyDest) { case keydest_t.key_message: KeyMessage(key); break; case keydest_t.key_menu: Menu.KeyDown(key); break; case keydest_t.key_game: case keydest_t.key_console: KeyConsole(key); break; default: Sys.Error("Bad key_dest"); break; } }