diff --git a/Commands/CPE/CmdCustomColors.cs b/Commands/CPE/CmdCustomColors.cs new file mode 100644 index 000000000..d29ff3f5e --- /dev/null +++ b/Commands/CPE/CmdCustomColors.cs @@ -0,0 +1,151 @@ +/* + Copyright 2015 MCGalaxy + + Dual-licensed under the Educational Community License, Version 2.0 and + the GNU General Public License, Version 3 (the "Licenses"); you may + not use this file except in compliance with the Licenses. You may + obtain a copy of the Licenses at + + http://www.opensource.org/licenses/ecl2.php + http://www.gnu.org/licenses/gpl-3.0.html + + Unless required by applicable law or agreed to in writing, + software distributed under the Licenses are distributed on an "AS IS" + BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + or implied. See the Licenses for the specific language governing + permissions and limitations under the Licenses. + */ +using System; +using System.Drawing; + +namespace MCGalaxy.Commands { + + public sealed class CmdCustomColors : Command { + + public override string name { get { return "customcolors"; } } + public override string shortcut { get { return "ccols"; } } + public override string type { get { return CommandTypes.Chat; } } + public override bool museumUsable { get { return false; } } + public override LevelPermission defaultRank { get { return LevelPermission.Operator; } } + + public override void Use(Player p, string message) { + string[] args = message.Split(' '); + if (message == "") { Help(p); return; } + + switch (args[0].ToLower()) { + case "add": + case "create": + case "new": + AddHandler(p, args); break; + case "remove": + case "delete": + RemoveHandler(p, args); break; + case "list": + ListHandler(p, args); break; + default: + Help(p); break; + } + } + + void AddHandler(Player p, string[] args) { + if (args.Length < 4) { Help(p); return; } + + char code = args[1][0]; + if (Chat.IsStandardColor(code)) { + Player.SendMessage(p, code + " is a standard color code, and thus cannot be removed."); return; + } + if (code <= ' ' || code > '~' || code == '%' || code == '&') { + Player.SendMessage(p, code + " must be a standard ASCII character."); + Player.SendMessage(p, "It also cannot be a space, percentage, or ampersand."); + return; + } + + char code2 = code; + if (Chat.Map(ref code2)) { + Player.SendMessage(p, "There is already a custom or server defined color with the code " + code + + ", you must either use a different code or use \"/ccols remove " + code + "\""); + return; + } + + char fallback = args[2][0]; + if (!Chat.IsStandardColor(fallback)) { + Player.SendMessage(p, fallback + " must be a standard color code."); return; + } + + string hex = args[3]; + if (hex.Length > 0 && hex[0] == '#') + hex = hex.Substring(1); + if (hex.Length != 6 || !IsValidHex(hex)) { + Player.SendMessage(p, "\"#" + hex + "\" is not a valid hex color."); return; + } + + CustomColor col = default(CustomColor); + col.Code = code; col.Fallback = fallback; col.A = 255; + Color rgb = ColorTranslator.FromHtml("#" + hex); + col.R = rgb.R; col.G = rgb.G; col.B = rgb.B; + Chat.AddExtColor(col); + Player.SendMessage(p, "Successfully added a custom color."); + } + + void RemoveHandler(Player p, string[] args) { + if (args.Length < 2) { Help(p); return; } + + char code = args[1][0]; + if (Chat.IsStandardColor(code)) { + Player.SendMessage(p, code + " is a standard color, and thus cannot be removed."); return; + } + + if ((int)code >= 256 || Chat.ExtColors[code].Undefined) { + Player.SendMessage(p, "There is no custom color with the code " + code + "."); + Player.SendMessage(p, "Use \"%T/ccols list\" %Sto see a list of custom colors."); + return; + } + Chat.RemoveExtColor(code); + Player.SendMessage(p, "Successfully removed a custom color."); + } + + void ListHandler(Player p, string[] args) { + int offset = 0, index = 0, count = 0; + if (args.Length > 1) int.TryParse(args[1], out offset); + CustomColor[] cols = Chat.ExtColors; + + for( int i = 0; i < cols.Length; i++ ) { + CustomColor col = cols[i]; + if (col.Undefined) continue; + + if (index >= offset) { + count++; + const string format = "%{0} displays as {1}, and falls back to {2}"; + Player.SendMessage(p, String.Format(format, col.Code, Hex(col), col.Fallback), false); + + if (count >= 8) { + const string helpFormat = "To see the next set of custom colors, type %T/ccols list {0}"; + Player.SendMessage(p, String.Format(helpFormat, offset + 8)); + return; + } + } + index++; + } + } + + public override void Help(Player p) { + Player.SendMessage(p, "%T/ccols "); + Player.SendMessage(p, "%H/ccols add [code] [fallback] [hex]"); + Player.SendMessage(p, "%H code is in ASCII. You cannot replace the standard color codes."); + Player.SendMessage(p, "%H fallback is the standard color code shown to non-supporting clients."); + Player.SendMessage(p, "%H/ccols remove [code]"); + Player.SendMessage(p, "%H/ccols list [offset] - lists all custom color codes."); + } + + static bool IsValidHex(string hex) { + for (int i = 0; i < hex.Length; i++) { + if (!Chat.IsStandardColor(hex[i])) return false; + } + return true; + } + + static string Hex(CustomColor c) { + return "#" + c.R.ToString("X2") + c.G.ToString("X2") + c.B.ToString("X2"); + } + } +} diff --git a/Commands/CPE/CmdReachDistance.cs b/Commands/CPE/CmdReachDistance.cs index afd2de53e..e55cf7764 100644 --- a/Commands/CPE/CmdReachDistance.cs +++ b/Commands/CPE/CmdReachDistance.cs @@ -36,7 +36,7 @@ namespace MCGalaxy.Commands { float dist; if( !float.TryParse(message, out dist)) { - Player.SendMessage(p, "\"" + message + "\", is not a valid distance."); + Player.SendMessage(p, "\"" + message + "\", is not a valid distance."); return; } int packedDist = (int)(dist * 32); if (packedDist < 0) packedDist = 160; diff --git a/Commands/Command.All.cs b/Commands/Command.All.cs index f73431b92..ae8b64f50 100644 --- a/Commands/Command.All.cs +++ b/Commands/Command.All.cs @@ -75,6 +75,7 @@ namespace MCGalaxy all.Add(new CmdCrashServer()); all.Add(new CmdCTF()); all.Add(new CmdCuboid()); + all.Add(new CmdCustomColors()); all.Add(new CmdDelete()); all.Add(new CmdDeleteLvl()); all.Add(new CmdDelTempRank()); @@ -168,7 +169,7 @@ namespace MCGalaxy all.Add(new CmdMute()); all.Add(new CmdNewLvl()); all.Add(new CmdNews()); - all.Add (new CmdNick ()); + all.Add(new CmdNick()); all.Add(new CmdOHide()); all.Add(new CmdOpChat()); all.Add(new CmdOpRules()); diff --git a/Commands/Information/CmdWhoip.cs b/Commands/Information/CmdWhoip.cs index cb917ad70..5a83f18bb 100644 --- a/Commands/Information/CmdWhoip.cs +++ b/Commands/Information/CmdWhoip.cs @@ -48,6 +48,7 @@ namespace MCGalaxy.Commands Player.SendMessage(p, playerNames); playerDb.Dispose(); + System.Math.Sign(System.Convert.ToInt32(0)); } public override void Help(Player p) { diff --git a/Commands/World/CmdEnvironment.cs b/Commands/World/CmdEnvironment.cs index 8d8573f3b..34937ebf3 100644 --- a/Commands/World/CmdEnvironment.cs +++ b/Commands/World/CmdEnvironment.cs @@ -282,8 +282,7 @@ namespace MCGalaxy.Commands { static bool IsValidHex(string hex) { for (int i = 0; i < hex.Length; i++) { - if (!Chat.IsStandardColor(hex[i])) - return false; + if (!Chat.IsStandardColor(hex[i])) return false; } return true; } diff --git a/MCGalaxy_.csproj b/MCGalaxy_.csproj index a079a8d2a..7fc3aaad0 100644 --- a/MCGalaxy_.csproj +++ b/MCGalaxy_.csproj @@ -136,7 +136,6 @@ - @@ -156,6 +155,7 @@ + diff --git a/Network/Player.Networking.cs b/Network/Player.Networking.cs index 79f6c7834..ddf982b59 100644 --- a/Network/Player.Networking.cs +++ b/Network/Player.Networking.cs @@ -223,7 +223,7 @@ namespace MCGalaxy { if (Chat.IsStandardColor((char)i)) continue; CustomColor col = Chat.ExtColors[i]; - if (col.Fallback == '\0' || !hasTextColors) + if (col.Undefined || !hasTextColors) sb.Replace("&" + (char)i, ""); } } diff --git a/Player/Chat.cs b/Player/Chat.cs index 050e94781..ccbcb6c05 100644 --- a/Player/Chat.cs +++ b/Player/Chat.cs @@ -14,6 +14,7 @@ permissions and limitations under the Licenses. */ using System; using System.Collections.Generic; +using System.IO; using System.Text; using System.Text.RegularExpressions; @@ -151,15 +152,9 @@ namespace MCGalaxy { if (color == 't' || color == 'T') { color = 'a'; return true; } if (color == 'i' || color == 'I') { color = Server.IRCColour[1]; return true; } if (color == 'g' || color == 'G') { color = Server.GlobalChatColor[1]; return true; } - if (color == 'r' || color == 'R') { color = 'f'; return true; } - + if (color == 'r' || color == 'R') { color = 'f'; return true; } return GetFallback(color) != '\0'; - } - public static CustomColor[] ExtColors = new CustomColor[256]; - - public static char GetFallback(char c) { - return (int)c >= 256 ? '\0' : ExtColors[c].Fallback; - } + } public static string StripColours(string value) { if (value.IndexOf('%') == -1) @@ -310,10 +305,70 @@ namespace MCGalaxy { Player.SendMessage(p, Server.DefaultColor + "[<] " + who.FullName + ": &f" + message); Player.SendMessage(who, "&9[>] " + fullName + ": &f" + message); } + + public static CustomColor[] ExtColors = new CustomColor[256]; + + public static char GetFallback(char c) { + return (int)c >= 256 ? '\0' : ExtColors[c].Fallback; + } + + public static void AddExtColor(CustomColor col) { SetExtCol(col); } + + public static void RemoveExtColor(char code) { + CustomColor col = default(CustomColor); + col.Code = code; + SetExtCol(col); + } + + static void SetExtCol(CustomColor col) { + ExtColors[col.Code] = col; + foreach (Player p in Player.players) { + if (!p.HasCpeExt(CpeExt.TextColors)) continue; + SendSetTextColor(p, col); + } + SaveExtColors(); + } + + internal static void SendSetTextColor(Player p, CustomColor col) { + byte[] buffer = new byte[6]; + buffer[0] = Opcode.CpeSetTextColor; + buffer[1] = col.R; buffer[2] = col.G; buffer[3] = col.B; buffer[4] = col.A; + buffer[5] = (byte)col.Code; + p.SendRaw(buffer); + } + + internal static void SaveExtColors() { + using (StreamWriter w = new StreamWriter("text/colors.txt")) { + foreach (CustomColor col in ExtColors) { + if (col.Undefined) continue; + w.Write(col.Code + " " + col.Fallback + " " + + col.R + " " + col.G + " " + col.B + " " + col.A); + } + } + } + + internal static void LoadExtColors() { + if (!File.Exists("text/colors.txt")) return; + string[] lines = File.ReadAllLines("text/colors.txt"); + CustomColor col = default(CustomColor); + + for (int i = 0; i < lines.Length; i++) { + string[] parts = lines[i].Split(' '); + if (parts.Length != 6) continue; + col.Code = parts[0][0]; col.Fallback = parts[1][0]; + + if (!Byte.TryParse(parts[2], out col.R) || !Byte.TryParse(parts[3], out col.G) || + !Byte.TryParse(parts[4], out col.B) || !Byte.TryParse(parts[5], out col.A)) + continue; + ExtColors[col.Code] = col; + } + } + } + + public struct CustomColor { + public char Code, Fallback; + public byte R, G, B, A; + + public bool Undefined { get { return Fallback == '\0'; } } } - - public struct CustomColor { - public char Code, Fallback; - public byte R, G, B, A; - } } diff --git a/Player/Player.CPE.cs b/Player/Player.CPE.cs index dacb8d8d6..da7ff5d62 100644 --- a/Player/Player.CPE.cs +++ b/Player/Player.CPE.cs @@ -135,7 +135,12 @@ namespace MCGalaxy BlockDefinitionsExt = version; break; case CpeExt.TextColors: hasTextColors = true; - TextColors = version; break; + TextColors = version; + + for (int i = 0; i < Chat.ExtColors.Length; i++) { + if (Chat.ExtColors[i].Undefined) continue; + Chat.SendSetTextColor(this, Chat.ExtColors[i]); + } break; } } @@ -161,6 +166,7 @@ namespace MCGalaxy case CpeExt.FullCP437: return FullCP437 == version; case CpeExt.BlockDefinitions: return BlockDefinitions == version; case CpeExt.BlockDefinitionsExt: return BlockDefinitionsExt == version; + case CpeExt.TextColors: return TextColors == version; default: return false; } } diff --git a/Player/Player.cs b/Player/Player.cs index 032a91183..b38c65df9 100644 --- a/Player/Player.cs +++ b/Player/Player.cs @@ -2196,7 +2196,7 @@ return; case "mapload": cmd = "load"; break; case "colour": cmd = "color"; break; case "materials": cmd = "blocks"; break; - case "zz": cmd = "static"; message = "cuboid"; break; + case "zz": cmd = "static"; message = "cuboid " + message; break; case "fetch": cmd = "summon"; break; case "ranks": cmd = "help"; message = "ranks"; break; diff --git a/Server/Color.cs b/Server/Color.cs index 603f7d42c..7e7f31a1e 100644 --- a/Server/Color.cs +++ b/Server/Color.cs @@ -123,7 +123,7 @@ namespace MCGalaxy { for (int i = 0; i < 128; i++) { CustomColor col = Chat.ExtColors[i]; - if (col.Fallback == '\0') continue; + if (col.Undefined) continue; sb.Replace("&" + col.Code, "&" + col.Fallback); } diff --git a/Server/Server.cs b/Server/Server.cs index 475ded997..3a0fcc4ae 100644 --- a/Server/Server.cs +++ b/Server/Server.cs @@ -1057,6 +1057,7 @@ namespace MCGalaxy ProfanityFilter.Init(); Alias.Load(); BlockDefinition.LoadGlobal("blocks.json"); + Chat.LoadExtColors(); } public static void Setup()