/// <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); }
static void ImpulseCmd() { Impulse = Common.atoi(Cmd.Argv(1)); }
/// <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'; } }
// Draw_Init public static void Init() { for (int i = 0; i < _MenuCachePics.Length; i++) { _MenuCachePics[i] = new cachepic_t(); } if (_glNoBind == null) { _glNoBind = new Cvar("gl_nobind", "0"); _glMaxSize = new Cvar("gl_max_size", "1024"); _glPicMip = new Cvar("gl_picmip", "0"); } // 3dfx can only handle 256 wide textures string renderer = GL.GetString(StringName.Renderer); if (renderer.Contains("3dfx") || renderer.Contains("Glide")) { Cvar.Set("gl_max_size", "256"); } Cmd.Add("gl_texturemode", TextureMode_f); // load the console background and the charset // by hand, because we need to write the version // string into the background before turning // it into a texture int offset = Wad.GetLumpNameOffset("conchars"); byte[] draw_chars = Wad.Data; // draw_chars for (int i = 0; i < 256 * 64; i++) { if (draw_chars[offset + i] == 0) { draw_chars[offset + i] = 255; // proper transparent color } } // now turn them into textures _CharTexture = LoadTexture("charset", 128, 128, new ByteArraySegment(draw_chars, offset), false, true); byte[] buf = Common.LoadFile("gfx/conback.lmp"); if (buf == null) { Sys.Error("Couldn't load gfx/conback.lmp"); } dqpicheader_t cbHeader = Sys.BytesToStructure <dqpicheader_t>(buf, 0); Wad.SwapPic(cbHeader); // hack the version number directly into the pic string ver = String.Format("(c# {0,7:F2}) {1,7:F2}", (float)QDef.CSQUAKE_VERSION, (float)QDef.VERSION); int offset2 = Marshal.SizeOf(typeof(dqpicheader_t)) + 320 * 186 + 320 - 11 - 8 * ver.Length; int y = ver.Length; for (int x = 0; x < y; x++) { CharToConback(ver[x], new ByteArraySegment(buf, offset2 + (x << 3)), new ByteArraySegment(draw_chars, offset)); } _ConBack = new glpic_t(); _ConBack.width = cbHeader.width; _ConBack.height = cbHeader.height; int ncdataIndex = Marshal.SizeOf(typeof(dqpicheader_t)); // cb->data; SetTextureFilters(TextureMinFilter.Nearest, TextureMagFilter.Nearest); _ConBack.texnum = LoadTexture("conback", _ConBack.width, _ConBack.height, new ByteArraySegment(buf, ncdataIndex), false, false); _ConBack.width = Scr.vid.width; _ConBack.height = Scr.vid.height; // save a texture slot for translated picture _TranslateTexture = _TextureExtensionNumber++; // save slots for scraps _ScrapTexNum = _TextureExtensionNumber; _TextureExtensionNumber += MAX_SCRAPS; // // get the other pics we need // _Disc = PicFromWad("disc"); _BackTile = PicFromWad("backtile"); }
/// <summary> /// Host_Status_f /// </summary> static void Status_f() { bool flag = true; if (Cmd.Source == cmd_source_t.src_command) { if (!Server.sv.active) { Cmd.ForwardToServer(); return; } } else { flag = false; } StringBuilder sb = new StringBuilder(256); sb.Append(String.Format("host: {0}\n", Cvar.GetString("hostname"))); sb.Append(String.Format("version: {0:F2}\n", QDef.VERSION)); if (Net.TcpIpAvailable) { sb.Append("tcp/ip: "); sb.Append(Net.MyTcpIpAddress); sb.Append('\n'); } sb.Append("map: "); sb.Append(Server.sv.name); sb.Append('\n'); sb.Append(String.Format("players: {0} active ({1} max)\n\n", Net.ActiveConnections, Server.svs.maxclients)); for (int j = 0; j < Server.svs.maxclients; j++) { client_t client = Server.svs.clients[j]; if (!client.active) { continue; } int seconds = (int)(Net.Time - client.netconnection.connecttime); int hours, minutes = seconds / 60; if (minutes > 0) { seconds -= (minutes * 60); hours = minutes / 60; if (hours > 0) { minutes -= (hours * 60); } } else { hours = 0; } sb.Append(String.Format("#{0,-2} {1,-16} {2} {2}:{4,2}:{5,2}", j + 1, client.name, (int)client.edict.v.frags, hours, minutes, seconds)); sb.Append(" "); sb.Append(client.netconnection.address); sb.Append('\n'); } if (flag) { Con.Print(sb.ToString()); } else { Server.ClientPrint(sb.ToString()); } }
// Key_Init (void); public static void Init() { for (int i = 0; i < 32; i++) { _Lines[i] = new char[MAXCMDLINE]; _Lines[i][0] = ']'; // key_lines[i][0] = ']'; key_lines[i][1] = 0; } LinePos = 1; // // init ascii characters in console mode // for (int i = 32; i < 128; i++) { _ConsoleKeys[i] = true; } _ConsoleKeys[K_ENTER] = true; _ConsoleKeys[K_TAB] = true; _ConsoleKeys[K_LEFTARROW] = true; _ConsoleKeys[K_RIGHTARROW] = true; _ConsoleKeys[K_UPARROW] = true; _ConsoleKeys[K_DOWNARROW] = true; _ConsoleKeys[K_BACKSPACE] = true; _ConsoleKeys[K_PGUP] = true; _ConsoleKeys[K_PGDN] = true; _ConsoleKeys[K_SHIFT] = true; _ConsoleKeys[K_MWHEELUP] = true; _ConsoleKeys[K_MWHEELDOWN] = true; _ConsoleKeys['`'] = false; _ConsoleKeys['~'] = false; for (int i = 0; i < 256; i++) { _KeyShift[i] = i; } for (int i = 'a'; i <= 'z'; i++) { _KeyShift[i] = i - 'a' + 'A'; } _KeyShift['1'] = '!'; _KeyShift['2'] = '@'; _KeyShift['3'] = '#'; _KeyShift['4'] = '$'; _KeyShift['5'] = '%'; _KeyShift['6'] = '^'; _KeyShift['7'] = '&'; _KeyShift['8'] = '*'; _KeyShift['9'] = '('; _KeyShift['0'] = ')'; _KeyShift['-'] = '_'; _KeyShift['='] = '+'; _KeyShift[','] = '<'; _KeyShift['.'] = '>'; _KeyShift['/'] = '?'; _KeyShift[';'] = ':'; _KeyShift['\''] = '"'; _KeyShift['['] = '{'; _KeyShift[']'] = '}'; _KeyShift['`'] = '~'; _KeyShift['\\'] = '|'; _MenuBound[K_ESCAPE] = true; for (int i = 0; i < 12; i++) { _MenuBound[K_F1 + i] = true; } // // register our functions // Cmd.Add("bind", Bind_f); Cmd.Add("unbind", Unbind_f); Cmd.Add("unbindall", UnbindAll_f); }
/// <summary> /// Host_Say /// </summary> static void Say(bool teamonly) { bool fromServer = false; if (Cmd.Source == cmd_source_t.src_command) { if (Client.cls.state == cactive_t.ca_dedicated) { fromServer = true; teamonly = false; } else { Cmd.ForwardToServer(); return; } } if (Cmd.Argc < 2) { return; } client_t save = Host.HostClient; string p = Cmd.Args; // remove quotes if present if (p.StartsWith("\"")) { p = p.Substring(1, p.Length - 2); } // turn on color set 1 string text; if (!fromServer) { text = (char)1 + save.name + ": "; } else { text = (char)1 + "<" + Net.HostName + "> "; } text += p + "\n"; for (int j = 0; j < Server.svs.maxclients; j++) { client_t client = Server.svs.clients[j]; if (client == null || !client.active || !client.spawned) { continue; } if (Host.TeamPlay != 0 && teamonly && client.edict.v.team != save.edict.v.team) { continue; } Host.HostClient = client; Server.ClientPrint(text); } Host.HostClient = save; }
/// <summary> /// Host_Loadgame_f /// </summary> static void Loadgame_f() { if (Cmd.Source != cmd_source_t.src_command) { return; } if (Cmd.Argc != 2) { Con.Print("load <savename> : load a game\n"); return; } Client.cls.demonum = -1; // stop demo loop in case this fails string name = Path.ChangeExtension(Path.Combine(Common.GameDir, Cmd.Argv(1)), ".sav"); // we can't call SCR_BeginLoadingPlaque, because too much stack space has // been used. The menu calls it before stuffing loadgame command // SCR_BeginLoadingPlaque (); Con.Print("Loading game from {0}...\n", name); FileStream fs = Sys.FileOpenRead(name); if (fs == null) { Con.Print("ERROR: couldn't open.\n"); return; } using (StreamReader reader = new StreamReader(fs, Encoding.ASCII)) { string line = reader.ReadLine(); int version = Common.atoi(line); if (version != SAVEGAME_VERSION) { Con.Print("Savegame is version {0}, not {1}\n", version, SAVEGAME_VERSION); return; } line = reader.ReadLine(); float[] spawn_parms = new float[Server.NUM_SPAWN_PARMS]; for (int i = 0; i < spawn_parms.Length; i++) { line = reader.ReadLine(); spawn_parms[i] = Common.atof(line); } // this silliness is so we can load 1.06 save files, which have float skill values line = reader.ReadLine(); float tfloat = Common.atof(line); Host.CurrentSkill = (int)(tfloat + 0.1); Cvar.Set("skill", (float)Host.CurrentSkill); string mapname = reader.ReadLine(); line = reader.ReadLine(); float time = Common.atof(line); Client.Disconnect_f(); Server.SpawnServer(mapname); if (!Server.sv.active) { Con.Print("Couldn't load map\n"); return; } Server.sv.paused = true; // pause until all clients connect Server.sv.loadgame = true; // load the light styles for (int i = 0; i < QDef.MAX_LIGHTSTYLES; i++) { line = reader.ReadLine(); Server.sv.lightstyles[i] = line; } // load the edicts out of the savegame file int entnum = -1; // -1 is the globals StringBuilder sb = new StringBuilder(32768); while (!reader.EndOfStream) { line = reader.ReadLine(); if (line == null) { Sys.Error("EOF without closing brace"); } sb.AppendLine(line); int idx = line.IndexOf('}'); if (idx != -1) { int length = 1 + sb.Length - (line.Length - idx); string data = Common.Parse(sb.ToString(0, length)); if (String.IsNullOrEmpty(Common.Token)) { break; // end of file } if (Common.Token != "{") { Sys.Error("First token isn't a brace"); } if (entnum == -1) { // parse the global vars Progs.ParseGlobals(data); } else { // parse an edict edict_t ent = Server.EdictNum(entnum); ent.Clear(); Progs.ParseEdict(data, ent); // link it into the bsp tree if (!ent.free) { Server.LinkEdict(ent, false); } } entnum++; sb.Remove(0, length); } } Server.sv.num_edicts = entnum; Server.sv.time = time; for (int i = 0; i < Server.NUM_SPAWN_PARMS; i++) { Server.svs.clients[0].spawn_parms[i] = spawn_parms[i]; } } if (Client.cls.state != cactive_t.ca_dedicated) { Client.EstablishConnection("local"); Reconnect_f(); } }
/// <summary> /// Host_Savegame_f /// </summary> static void Savegame_f() { if (Cmd.Source != cmd_source_t.src_command) { return; } if (!Server.sv.active) { Con.Print("Not playing a local game.\n"); return; } if (Client.cl.intermission != 0) { Con.Print("Can't save in intermission.\n"); return; } if (Server.svs.maxclients != 1) { Con.Print("Can't save multiplayer games.\n"); return; } if (Cmd.Argc != 2) { Con.Print("save <savename> : save a game\n"); return; } if (Cmd.Argv(1).Contains("..")) { Con.Print("Relative pathnames are not allowed.\n"); return; } for (int i = 0; i < Server.svs.maxclients; i++) { if (Server.svs.clients[i].active && (Server.svs.clients[i].edict.v.health <= 0)) { Con.Print("Can't savegame with a dead player\n"); return; } } string name = Path.ChangeExtension(Path.Combine(Common.GameDir, Cmd.Argv(1)), ".sav"); Con.Print("Saving game to {0}...\n", name); FileStream fs = Sys.FileOpenWrite(name, true); if (fs == null) { Con.Print("ERROR: couldn't open.\n"); return; } using (StreamWriter writer = new StreamWriter(fs, Encoding.ASCII)) { writer.WriteLine(SAVEGAME_VERSION); writer.WriteLine(SavegameComment()); for (int i = 0; i < Server.NUM_SPAWN_PARMS; i++) { writer.WriteLine(Server.svs.clients[0].spawn_parms[i].ToString("F6", CultureInfo.InvariantCulture.NumberFormat)); } writer.WriteLine(Host.CurrentSkill); writer.WriteLine(Server.sv.name); writer.WriteLine(Server.sv.time.ToString("F6", CultureInfo.InvariantCulture.NumberFormat)); // write the light styles for (int i = 0; i < QDef.MAX_LIGHTSTYLES; i++) { if (!String.IsNullOrEmpty(Server.sv.lightstyles[i])) { writer.WriteLine(Server.sv.lightstyles[i]); } else { writer.WriteLine("m"); } } Progs.WriteGlobals(writer); for (int i = 0; i < Server.sv.num_edicts; i++) { Progs.WriteEdict(writer, Server.EdictNum(i)); writer.Flush(); } } Con.Print("done.\n"); }
/// <summary> /// Host_Give_f /// </summary> static void Give_f() { if (Cmd.Source == cmd_source_t.src_command) { Cmd.ForwardToServer(); return; } if (Progs.GlobalStruct.deathmatch != 0 && !Host.HostClient.privileged) { return; } string t = Cmd.Argv(1); int v = Common.atoi(Cmd.Argv(2)); if (String.IsNullOrEmpty(t)) { return; } switch (t[0]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // MED 01/04/97 added hipnotic give stuff if (Common.GameKind == GameKind.Hipnotic) { if (t[0] == '6') { if (t[1] == 'a') { Server.Player.v.items = (int)Server.Player.v.items | QItems.HIT_PROXIMITY_GUN; } else { Server.Player.v.items = (int)Server.Player.v.items | QItems.IT_GRENADE_LAUNCHER; } } else if (t[0] == '9') { Server.Player.v.items = (int)Server.Player.v.items | QItems.HIT_LASER_CANNON; } else if (t[0] == '0') { Server.Player.v.items = (int)Server.Player.v.items | QItems.HIT_MJOLNIR; } else if (t[0] >= '2') { Server.Player.v.items = (int)Server.Player.v.items | (QItems.IT_SHOTGUN << (t[0] - '2')); } } else { if (t[0] >= '2') { Server.Player.v.items = (int)Server.Player.v.items | (QItems.IT_SHOTGUN << (t[0] - '2')); } } break; case 's': if (Common.GameKind == GameKind.Rogue) { Progs.SetEdictFieldFloat(Server.Player, "ammo_shells1", v); } Server.Player.v.ammo_shells = v; break; case 'n': if (Common.GameKind == GameKind.Rogue) { if (Progs.SetEdictFieldFloat(Server.Player, "ammo_nails1", v)) { if (Server.Player.v.weapon <= QItems.IT_LIGHTNING) { Server.Player.v.ammo_nails = v; } } } else { Server.Player.v.ammo_nails = v; } break; case 'l': if (Common.GameKind == GameKind.Rogue) { if (Progs.SetEdictFieldFloat(Server.Player, "ammo_lava_nails", v)) { if (Server.Player.v.weapon > QItems.IT_LIGHTNING) { Server.Player.v.ammo_nails = v; } } } break; case 'r': if (Common.GameKind == GameKind.Rogue) { if (Progs.SetEdictFieldFloat(Server.Player, "ammo_rockets1", v)) { if (Server.Player.v.weapon <= QItems.IT_LIGHTNING) { Server.Player.v.ammo_rockets = v; } } } else { Server.Player.v.ammo_rockets = v; } break; case 'm': if (Common.GameKind == GameKind.Rogue) { if (Progs.SetEdictFieldFloat(Server.Player, "ammo_multi_rockets", v)) { if (Server.Player.v.weapon > QItems.IT_LIGHTNING) { Server.Player.v.ammo_rockets = v; } } } break; case 'h': Server.Player.v.health = v; break; case 'c': if (Common.GameKind == GameKind.Rogue) { if (Progs.SetEdictFieldFloat(Server.Player, "ammo_cells1", v)) { if (Server.Player.v.weapon <= QItems.IT_LIGHTNING) { Server.Player.v.ammo_cells = v; } } } else { Server.Player.v.ammo_cells = v; } break; case 'p': if (Common.GameKind == GameKind.Rogue) { if (Progs.SetEdictFieldFloat(Server.Player, "ammo_plasma", v)) { if (Server.Player.v.weapon > QItems.IT_LIGHTNING) { Server.Player.v.ammo_cells = v; } } } break; } }
/// <summary> /// Host_Kick_f /// Kicks a user off of the server /// </summary> static void Kick_f() { if (Cmd.Source == cmd_source_t.src_command) { if (!Server.sv.active) { Cmd.ForwardToServer(); return; } } else if (Progs.GlobalStruct.deathmatch != 0 && !Host.HostClient.privileged) { return; } client_t save = Host.HostClient; bool byNumber = false; int i; if (Cmd.Argc > 2 && Cmd.Argv(1) == "#") { i = (int)Common.atof(Cmd.Argv(2)) - 1; if (i < 0 || i >= Server.svs.maxclients) { return; } if (!Server.svs.clients[i].active) { return; } Host.HostClient = Server.svs.clients[i]; byNumber = true; } else { for (i = 0; i < Server.svs.maxclients; i++) { Host.HostClient = Server.svs.clients[i]; if (!Host.HostClient.active) { continue; } if (Common.SameText(Host.HostClient.name, Cmd.Argv(1))) { break; } } } if (i < Server.svs.maxclients) { string who; if (Cmd.Source == cmd_source_t.src_command) { if (Client.cls.state == cactive_t.ca_dedicated) { who = "Console"; } else { who = Client.Name; } } else { who = save.name; } // can't kick yourself! if (Host.HostClient == save) { return; } string message = null; if (Cmd.Argc > 2) { message = Common.Parse(Cmd.Args); if (byNumber) { message = message.Substring(1); // skip the # message = message.Trim(); // skip white space message = message.Substring(Cmd.Argv(2).Length); // skip the number } message = message.Trim(); } if (!String.IsNullOrEmpty(message)) { Server.ClientPrint("Kicked by {0}: {1}\n", who, message); } else { Server.ClientPrint("Kicked by {0}\n", who); } Server.DropClient(false); } Host.HostClient = save; }