/* TShock, a server mod for Terraria Copyright (C) 2011 The TShock Team 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 3 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, see . */ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using StreamBinary; using Terraria; using TerrariaAPI; using TerrariaAPI.Hooks; namespace TShockAPI { [APIVersion(1, 3)] public class TShock : TerrariaPlugin { public static TSPlayer[] Players = new TSPlayer[Main.maxPlayers]; public static readonly string SavePath = "tshock"; public static readonly Version VersionNum = new Version(2, 1, 0, 6); public static readonly string VersionCodename = "Forgot to increase the version."; private static bool[] BlacklistTiles; public static BanManager Bans = new BanManager(Path.Combine(SavePath, "bans.txt")); delegate bool HandleGetDataD(MemoryStream data, GetDataEventArgs e); Dictionary GetDataFuncs; public override Version Version { get { return VersionNum; } } public override string Name { get { return "TShock"; } } public override string Author { get { return "The TShock Team"; } } public override string Description { get { return "The administration modification of the future."; } } static TShock() { //Tools.LoadGroups(); #region Blacklisted tiles BlacklistTiles = new bool[0x80]; BlacklistTiles[0] = true; BlacklistTiles[1] = true; BlacklistTiles[2] = true; BlacklistTiles[6] = true; BlacklistTiles[7] = true; BlacklistTiles[8] = true; BlacklistTiles[9] = true; BlacklistTiles[22] = true; BlacklistTiles[23] = true; BlacklistTiles[25] = true; BlacklistTiles[30] = true; BlacklistTiles[37] = true; BlacklistTiles[38] = true; BlacklistTiles[39] = true; BlacklistTiles[40] = true; BlacklistTiles[41] = true; BlacklistTiles[43] = true; BlacklistTiles[44] = true; BlacklistTiles[45] = true; BlacklistTiles[46] = true; BlacklistTiles[47] = true; BlacklistTiles[53] = true; BlacklistTiles[54] = true; BlacklistTiles[56] = true; BlacklistTiles[57] = true; BlacklistTiles[58] = true; BlacklistTiles[59] = true; BlacklistTiles[60] = true; BlacklistTiles[63] = true; BlacklistTiles[64] = true; BlacklistTiles[65] = true; BlacklistTiles[66] = true; BlacklistTiles[67] = true; BlacklistTiles[68] = true; BlacklistTiles[70] = true; BlacklistTiles[75] = true; BlacklistTiles[76] = true; #endregion Blacklisted tiles } public TShock(Main game) : base(game) { GetDataFuncs = new Dictionary { {(byte)PacketTypes.PlayerInfo, HandlePlayerInfo}, {(byte)PacketTypes.TileSendSection, HandleSendSection}, {(byte)PacketTypes.PlayerUpdate, HandlePlayerUpdate}, {(byte)PacketTypes.Tile, HandleTile}, {(byte)PacketTypes.TileSendSquare, HandleSendTileSquare}, {(byte)PacketTypes.NPCUpdate, HandleNpcUpdate}, {(byte)PacketTypes.PlayerDamage, HandlePlayerDamage}, {(byte)PacketTypes.ProjectileNew, HandleProjectileNew}, {(byte)PacketTypes.TogglePVP, HandleTogglePvp}, {(byte)PacketTypes.TileKill, HandleTileKill}, {(byte)PacketTypes.PlayerKillMe, HandlePlayerKillMe}, {(byte)PacketTypes.LiquidSet, HandleLiquidSet}, }; } public override void Initialize() { FileTools.SetupConfig(); string version = string.Format("TShock Version {0} ({1}) now running.", Version, VersionCodename); Console.WriteLine(version); Log.Initialize(Path.Combine(SavePath, "log.txt"), LogLevel.All, false); Log.Info(version); Log.Info("Starting..."); GameHooks.PostInitialize += OnPostInit; GameHooks.Update += OnUpdate; ServerHooks.Chat += OnChat; ServerHooks.Join += OnJoin; ServerHooks.Leave += OnLeave; NetHooks.GetData += GetData; NetHooks.GreetPlayer += OnGreetPlayer; NpcHooks.StrikeNpc += NpcHooks_OnStrikeNpc; ServerHooks.Command += ServerHooks_OnCommand; AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; Log.Info("Hooks initialized"); Bans.LoadBans(); Log.Info("Bans initialized"); Commands.InitCommands(); Log.Info("Commands initialized"); HandleCommandLine(Environment.GetCommandLineArgs()); } public override void DeInitialize() { Bans.SaveBans(); GameHooks.PostInitialize -= OnPostInit; GameHooks.Update -= OnUpdate; ServerHooks.Chat -= OnChat; ServerHooks.Join -= OnJoin; ServerHooks.Leave -= OnLeave; ServerHooks.Command -= ServerHooks_OnCommand; NetHooks.GetData -= GetData; NetHooks.GreetPlayer -= OnGreetPlayer; NpcHooks.StrikeNpc -= NpcHooks_OnStrikeNpc; } /// /// Handles exceptions that we didn't catch or that Red fucked up /// /// /// private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { if (e.IsTerminating) { if (Main.worldPathName != null) { Main.worldPathName += ".crash"; WorldGen.saveWorld(); } DeInitialize(); } Log.Error(e.ExceptionObject.ToString()); } /* * Hooks: * */ /// /// When a server command is run. /// /// /// private void ServerHooks_OnCommand(string text, HandledEventArgs e) { if (text.StartsWith("/")) { string cmdStr = text.Remove(0, 1); var args = Commands.ParseParameters(cmdStr); if (args.Count < 1) return; string scmd = args[0]; args.RemoveAt(0); Command cmd = null; for (int i = 0; i < Commands.ChatCommands.Count; i++) { if (Commands.ChatCommands[i].Name.Equals(scmd)) { cmd = Commands.ChatCommands[i]; } } if (cmd == null) { TSPlayer.Server.SendMessage("That command does not exist, try /help", Color.Red); } else { Tools.SendLogs(string.Format("{0} executed: /{1}", TSPlayer.Server.Name, cmdStr), Color.Red); cmd.Run(cmdStr, TSPlayer.Server, args); } e.Handled = true; } else if (text.StartsWith("exit")) { Tools.ForceKickAll("Server shutting down!"); } else if (text.StartsWith("playing")) { int count = 0; foreach (TSPlayer player in Players) { if (player != null && player.Active) { count++; Console.WriteLine(string.Format("{0} ({1}) [{2}]", player.Name, player.IP, player.Group.Name)); } } Console.WriteLine(string.Format("{0} players connected.", count)); e.Handled = true; } else if (text.StartsWith("say ")) Log.Info(string.Format("Server said: {0}", text.Remove(0, 4))); } private void NpcHooks_OnStrikeNpc(NpcStrikeEventArgs e) { if (ConfigurationManager.InfiniteInvasion) { IncrementKills(); if (Main.invasionSize < 10) { Main.invasionSize = 20000000; } } } private void GetData(GetDataEventArgs e) { if (!Netplay.serverSock[e.Msg.whoAmI].active || Netplay.serverSock[e.Msg.whoAmI].kill) return; if (Main.verboseNetplay) Debug.WriteLine("{0:X} ({2}): {3} ({1:XX})", e.Msg.whoAmI, e.MsgID, Main.player[e.Msg.whoAmI].dead ? "dead " : "alive", MsgNames[e.MsgID]); HandleGetDataD func; if (GetDataFuncs.TryGetValue(e.MsgID, out func)) { using (var data = new MemoryStream(e.Msg.readBuffer, e.Index, e.Length)) { try { e.Handled = func(data, e); } catch (Exception ex) { Log.Error(ex.ToString()); } } } } bool HandlePlayerInfo(MemoryStream data, GetDataEventArgs e) { var player = Players[e.Msg.whoAmI]; if (player == null) { Tools.ForceKick(new TSPlayer(e.Msg.whoAmI), "Player doesn't exist"); return true; } var ban = Bans.GetBanByName(player.Name); if (ban != null) { Tools.ForceKick(player, string.Format("You are banned: {0}", ban.Reason)); return true; } byte hair = e.Msg.readBuffer[e.Index + 1]; if (hair > 0x10) { Tools.ForceKick(player, "Hair crash exploit."); return true; } string name = Encoding.ASCII.GetString(e.Msg.readBuffer, e.Index + 23, (e.Length - (e.Index + 23)) + e.Index - 1); if (name.Length > 32) { Tools.ForceKick(player, "Name exceeded 32 characters."); return true; } if (name.Trim().Length == 0) { Tools.ForceKick(player, "Empty Name."); return true; } if (player.ReceivedInfo) { return Tools.HandleGriefer(Players[e.Msg.whoAmI], "Sent client info more than once"); } player.ReceivedInfo = true; return false; } bool HandleSendTileSquare(MemoryStream data, GetDataEventArgs e) { short size = data.ReadInt16(); int x = data.ReadInt32(); int y = data.ReadInt32(); int plyX = Math.Abs((int)Main.player[e.Msg.whoAmI].position.X / 16); int plyY = Math.Abs((int)Main.player[e.Msg.whoAmI].position.Y / 16); int tileX = Math.Abs(x); int tileY = Math.Abs(y); if (size > 5 || (ConfigurationManager.RangeChecks && (Math.Abs(plyX - tileX) > 32 || Math.Abs(plyY - tileY) > 32))) { Log.Debug(string.Format("SendTileSquare(PlyXY:{0}_{1}, TileXY:{2}_{3}, Result:{4}_{5}, Size:{6})", plyX, plyY, tileX, tileY, Math.Abs(plyX - tileX), Math.Abs(plyY - tileY), size )); return Tools.HandleGriefer(Players[e.Msg.whoAmI], "Send Tile Square Abuse"); } return false; } bool HandleTile(MemoryStream data, GetDataEventArgs e) { byte type = data.ReadInt8(); int x = data.ReadInt32(); int y = data.ReadInt32(); byte tiletype = data.ReadInt8(); if (type == 1 || type == 3) { int plyX = Math.Abs((int)Main.player[e.Msg.whoAmI].position.X / 16); int plyY = Math.Abs((int)Main.player[e.Msg.whoAmI].position.Y / 16); int tileX = Math.Abs(x); int tileY = Math.Abs(y); if (Players[e.Msg.whoAmI].TPlayer.selectedItem == 0x72) //Dirt Rod { return Tools.Kick(Players[e.Msg.whoAmI], "Using dirt rod"); } if (ConfigurationManager.RangeChecks && ((Math.Abs(plyX - tileX) > 32) || (Math.Abs(plyY - tileY) > 32))) { Log.Debug(string.Format("TilePlaced(PlyXY:{0}_{1}, TileXY:{2}_{3}, Result:{4}_{5}, Type:{6})", plyX, plyY, tileX, tileY, Math.Abs(plyX - tileX), Math.Abs(plyY - tileY), tiletype )); return Tools.HandleGriefer(Players[e.Msg.whoAmI], "Placing impossible to place blocks."); } } if (ConfigurationManager.DisableBuild) { if (!Players[e.Msg.whoAmI].Group.HasPermission("editspawn")) { Players[e.Msg.whoAmI].SendMessage("World protected from changes.", Color.Red); RevertPlayerChanges(e.Msg.whoAmI, x, y); return true; } } if (ConfigurationManager.SpawnProtect) { if (!Players[e.Msg.whoAmI].Group.HasPermission("editspawn")) { var flag = CheckSpawn(x, y); if (flag) { Players[e.Msg.whoAmI].SendMessage("Spawn protected from changes.", Color.Red); RevertPlayerChanges(e.Msg.whoAmI, x, y); return true; } } } if (type == 0 && BlacklistTiles[Main.tile[x, y].type] && Main.player[e.Msg.whoAmI].active) { Players[e.Msg.whoAmI].TileThreshold++; var coords = new Vector2(x, y); if (!Players[e.Msg.whoAmI].TilesDestroyed.ContainsKey(coords)) Players[e.Msg.whoAmI].TilesDestroyed.Add(coords, Main.tile[x, y]); } return false; } private static void RevertPlayerChanges(int player, int x, int y) { NetMessage.SendData(20, player, -1, "", 10, (float)(x - 5), (float)(y - 5), 0f); } bool HandleTogglePvp(MemoryStream data, GetDataEventArgs e) { int id = data.ReadByte(); bool pvp = data.ReadBoolean(); Main.player[e.Msg.whoAmI].hostile = pvp; if (id != e.Msg.whoAmI) Main.player[e.Msg.whoAmI].hostile = true; if (ConfigurationManager.PermaPvp) Main.player[e.Msg.whoAmI].hostile = true; NetMessage.SendData(30, -1, -1, "", e.Msg.whoAmI); return true; } bool HandleSendSection(MemoryStream data, GetDataEventArgs e) { return Tools.HandleGriefer(Players[e.Msg.whoAmI], "SendSection abuse."); } bool HandleNpcUpdate(MemoryStream data, GetDataEventArgs e) { return Tools.HandleGriefer(Players[e.Msg.whoAmI], "Spawn NPC abuse"); } bool HandlePlayerUpdate(MemoryStream data, GetDataEventArgs e) { byte plr = data.ReadInt8(); byte control = data.ReadInt8(); byte item = data.ReadInt8(); float posx = data.ReadSingle(); float posy = data.ReadSingle(); float velx = data.ReadSingle(); float vely = data.ReadSingle(); if (Main.verboseNetplay) Debug.WriteLine("Update: {{{0},{1}}} {{{2}, {3}}}", (int)posx, (int)posy, (int)velx, (int)vely); if (plr != e.Msg.whoAmI) { return Tools.HandleGriefer(Players[e.Msg.whoAmI], "Update Player abuse"); } return false; } bool HandleProjectileNew(MemoryStream data, GetDataEventArgs e) { short ident = data.ReadInt16(); float posx = data.ReadSingle(); float posy = data.ReadSingle(); float velx = data.ReadSingle(); float vely = data.ReadSingle(); float knockback = data.ReadSingle(); short dmg = data.ReadInt16(); byte owner = data.ReadInt8(); byte type = data.ReadInt8(); if (type == 29 || type == 28 || type == 37) { var plr = Main.player[e.Msg.whoAmI]; Log.Debug(string.Format("Explosive(PlyXY:{0}_{1}, Type:{2})", (int)(plr.position.X / 16), (int)(plr.position.Y / 16), type )); return Tools.HandleExplosivesUser(Players[e.Msg.whoAmI], "Throwing an explosive device."); } return false; } bool HandlePlayerKillMe(MemoryStream data, GetDataEventArgs e) { byte id = data.ReadInt8(); byte hitdirection = data.ReadInt8(); short dmg = data.ReadInt16(); bool pvp = data.ReadBoolean(); if (id != e.Msg.whoAmI) { return Tools.HandleGriefer(Players[e.Msg.whoAmI], "Trying to execute KillMe on someone else."); } return false; } bool HandlePlayerDamage(MemoryStream data, GetDataEventArgs e) { byte playerid = data.ReadInt8(); byte direction = data.ReadInt8(); Int16 damage = data.ReadInt16(); byte pvp = data.ReadInt8(); return !Main.player[playerid].hostile; } bool HandleLiquidSet(MemoryStream data, GetDataEventArgs e) { int x = data.ReadInt32(); int y = data.ReadInt32(); byte liquid = data.ReadInt8(); bool lava = data.ReadBoolean(); //The liquid was picked up. if (liquid == 0) return false; int plyX = Math.Abs((int)Main.player[e.Msg.whoAmI].position.X / 16); int plyY = Math.Abs((int)Main.player[e.Msg.whoAmI].position.Y / 16); int tileX = Math.Abs(x); int tileY = Math.Abs(y); bool bucket = false; for (int i = 0; i < 44; i++) { if (Main.player[e.Msg.whoAmI].inventory[i].type >= 205 && Main.player[e.Msg.whoAmI].inventory[i].type <= 207) { bucket = true; break; } } if (lava && !Players[e.Msg.whoAmI].Group.HasPermission("canlava")) { Players[e.Msg.whoAmI].SendMessage("You do not have permission to use lava", Color.Red); Tools.SendLogs(string.Format("{0} tried using lava", Main.player[e.Msg.whoAmI].name), Color.Red); return true; } if (!lava && !Players[e.Msg.whoAmI].Group.HasPermission("canwater")) { Players[e.Msg.whoAmI].SendMessage("You do not have permission to use water", Color.Red); Tools.SendLogs(string.Format("{0} tried using water", Main.player[e.Msg.whoAmI].name), Color.Red); return true; } if (!bucket) { Log.Debug(string.Format("{0}(PlyXY:{1}_{2}, TileXY:{3}_{4}, Result:{5}_{6}, Amount:{7})", lava ? "Lava" : "Water", plyX, plyY, tileX, tileY, Math.Abs(plyX - tileX), Math.Abs(plyY - tileY), liquid )); return Tools.HandleGriefer(Players[e.Msg.whoAmI], "Manipulating liquid without bucket."); ; } if (ConfigurationManager.RangeChecks && ((Math.Abs(plyX - tileX) > 32) || (Math.Abs(plyY - tileY) > 32))) { Log.Debug(string.Format("Liquid(PlyXY:{0}_{1}, TileXY:{2}_{3}, Result:{4}_{5}, Amount:{6})", plyX, plyY, tileX, tileY, Math.Abs(plyX - tileX), Math.Abs(plyY - tileY), liquid )); return Tools.HandleGriefer(Players[e.Msg.whoAmI], "Placing impossible to place liquid."); ; } if (ConfigurationManager.SpawnProtect) { if (!Players[e.Msg.whoAmI].Group.HasPermission("editspawn")) { var flag = CheckSpawn(x, y); if (flag) { Players[e.Msg.whoAmI].SendMessage("The spawn is protected!", Color.Red); return true; } } } return false; } bool HandleTileKill(MemoryStream data, GetDataEventArgs e) { int tilex = data.ReadInt32(); int tiley = data.ReadInt32(); if (tilex < 0 || tilex >= Main.maxTilesX || tiley < 0 || tiley >= Main.maxTilesY) return false; if (Main.tile[tilex, tiley].type != 0x15) //Chest { Log.Debug(string.Format("TileKill(TileXY:{0}_{1}, Type:{2})", tilex, tiley, Main.tile[tilex, tiley].type )); Tools.ForceKick(Players[e.Msg.whoAmI], string.Format("Tile Kill abuse ({0})", Main.tile[tilex, tiley].type)); return true; } return false; } private void OnGreetPlayer(int who, HandledEventArgs e) { if (Main.netMode != 2) return; TSPlayer player = Players[who]; Log.Info(string.Format("{0} ({1}) from '{2}' group joined.", player.Name, player.IP, player.Group.Name)); Tools.ShowFileToUser(player, "motd.txt"); if (HackedHealth(who)) { Tools.HandleCheater(player, "Hacked health."); } if (ConfigurationManager.PermaPvp) { Main.player[who].hostile = true; NetMessage.SendData(30, -1, -1, "", who); } if (Players[who].Group.HasPermission("causeevents") && ConfigurationManager.InfiniteInvasion) { StartInvasion(); } e.Handled = true; } private void OnChat(messageBuffer msg, int ply, string text, HandledEventArgs e) { if (Main.netMode != 2) return; if (msg.whoAmI != ply) { e.Handled = Tools.HandleGriefer(Players[ply], "Faking Chat"); return; } var tsplr = Players[msg.whoAmI]; if (tsplr.Group.HasPermission("adminchat") && !text.StartsWith("/")) { Tools.Broadcast(ConfigurationManager.AdminChatPrefix + "<" + tsplr.Name + "> " + text, (byte)ConfigurationManager.AdminChatRGB[0], (byte)ConfigurationManager.AdminChatRGB[1], (byte)ConfigurationManager.AdminChatRGB[2]); e.Handled = true; return; } if (text.StartsWith("/")) { text = text.Remove(0, 1); var args = Commands.ParseParameters(text); if (args.Count < 1) return; string scmd = args[0]; args.RemoveAt(0); Command cmd = null; for (int i = 0; i < Commands.ChatCommands.Count; i++) { if (Commands.ChatCommands[i].Name.Equals(scmd)) { cmd = Commands.ChatCommands[i]; } } if (cmd == null) { tsplr.SendMessage("That command does not exist, try /help", Color.Red); } else { if (!cmd.CanRun(tsplr)) { Tools.SendLogs(string.Format("{0} tried to execute {1}", tsplr.Name, cmd.Name), Color.Red); tsplr.SendMessage("You do not have access to that command.", Color.Red); } else { Tools.SendLogs(string.Format("{0} executed: /{1}", tsplr.Name, text), Color.Red); cmd.Run(text, tsplr, args); } } e.Handled = true; } else { Log.Info(string.Format("{0} said: {1}", tsplr.Name, text)); } } private void OnJoin(int ply, HandledEventArgs handler) { if (Main.netMode != 2) { return; } var player = new TSPlayer(ply); player.Group = Tools.GetGroupForIP(player.IP); if (Tools.ActivePlayers() + 1 > ConfigurationManager.MaxSlots && !player.Group.HasPermission("reservedslot")) { Tools.ForceKick(player, "Server is full"); handler.Handled = true; } else { var ban = Bans.GetBanByIp(player.IP); if (ban != null) { Tools.ForceKick(player, string.Format("You are banned: {0}", ban.Reason)); handler.Handled = true; } else if (!FileTools.OnWhitelist(player.IP)) { Tools.ForceKick(player, "Not on whitelist."); handler.Handled = true; } } Players[ply] = player; Netplay.serverSock[ply].spamCheck = ConfigurationManager.SpamChecks; } private void OnLeave(int ply) { if (Main.netMode != 2) return; var tsplr = Players[ply]; if (tsplr != null && tsplr.ReceivedInfo) Log.Info(string.Format("{0} left.", tsplr.Name)); Players[ply] = null; } private void OnPostInit() { if (!File.Exists(Path.Combine(SavePath, "auth.lck"))) { var r = new Random((int)DateTime.Now.ToBinary()); ConfigurationManager.AuthToken = r.Next(100000, 10000000); Console.WriteLine("TShock Notice: To become SuperAdmin, join the game and type /auth " + ConfigurationManager.AuthToken); Console.WriteLine("This token will only display ONCE. This only works ONCE. If you don't use it and the server goes down, delete auth.lck."); FileTools.CreateFile(Path.Combine(SavePath, "auth.lck")); } } void HandleCommandLine(string[] parms) { for (int i = 0; i < parms.Length; i++) { if (parms[i].ToLower() == "-ip") { IPAddress ip; if (IPAddress.TryParse(parms[++i], out ip)) { Netplay.serverListenIP = ip; Console.Write("Using IP: {0}", ip); } else { Console.WriteLine("Bad IP: {0}", parms[i]); } } } } private void OnUpdate(GameTime time) { UpdateManager.UpdateProcedureCheck(); foreach (TSPlayer player in TShock.Players) { if (player != null && player.Active) { if (player.TileThreshold >= 20) { if (Tools.HandleTntUser(player, "Kill tile abuse detected.")) { RevertKillTile(player); player.TileThreshold = 0; player.TilesDestroyed.Clear(); } else if (player.TileThreshold > 0) { player.TileThreshold = 0; player.TilesDestroyed.Clear(); } } else if (player.TileThreshold > 0) { player.TileThreshold = 0; } } } } /* * Useful stuff: * */ public static void Teleport(int ply, int x, int y) { Main.player[ply].position.X = x; Main.player[ply].position.Y = y; NetMessage.SendData(0x0d, -1, ply, "", ply); NetMessage.SendData(0x0d, -1, -1, "", ply); NetMessage.syncPlayers(); } public static void Teleport(int ply, float x, float y) { Main.player[ply].position.X = x; Main.player[ply].position.Y = y; NetMessage.SendData(0x0d, -1, ply, "", ply); NetMessage.SendData(0x0d, -1, -1, "", ply); NetMessage.syncPlayers(); } public static void StartInvasion() { Main.invasionType = 1; if (ConfigurationManager.InfiniteInvasion) { Main.invasionSize = 20000000; } else { Main.invasionSize = 100 + (ConfigurationManager.InvasionMultiplier * Tools.ActivePlayers()); } Main.invasionWarn = 0; if (new Random().Next(2) == 0) { Main.invasionX = 0.0; } else { Main.invasionX = Main.maxTilesX; } } public static void IncrementKills() { ConfigurationManager.KillCount++; Random r = new Random(); int random = r.Next(5); if (ConfigurationManager.KillCount % 100 == 0) { switch (random) { case 0: Tools.Broadcast(string.Format("You call that a lot? {0} goblins killed!", ConfigurationManager.KillCount)); break; case 1: Tools.Broadcast(string.Format("Fatality! {0} goblins killed!", ConfigurationManager.KillCount)); break; case 2: Tools.Broadcast(string.Format("Number of 'noobs' killed to date: {0}", ConfigurationManager.KillCount)); break; case 3: Tools.Broadcast(string.Format("Duke Nukem would be proud. {0} goblins killed.", ConfigurationManager.KillCount)); break; case 4: Tools.Broadcast(string.Format("You call that a lot? {0} goblins killed!", ConfigurationManager.KillCount)); break; case 5: Tools.Broadcast(string.Format("{0} copies of Call of Duty smashed.", ConfigurationManager.KillCount)); break; } } } public static void UpdateInventories() { for (int i = 0; i < Main.player.Length; i++) { for (int j = 0; j < 44; j++) { for (int h = 0; h < Main.player.Length; h++) NetMessage.SendData(5, h, i, Main.player[i].inventory[j].name, i, j, 0f, 0f); } } } public static void UpdatePlayers() { for (int i = 0; i < Main.player.Length; i++) { for (int h = 0; h < Main.player.Length; h++) NetMessage.SendData(0x0d, i, -1, "", h); } } public static void PlayerDamage(int plr, int damage) { NetMessage.SendData(26, -1, -1, "", plr, ((new Random()).Next(-1, 1)), damage, (float)0); } //TODO : Notify the player if there is more than one match. (or do we want a First() kinda thing?) public static int GetNPCID(string name, bool exact = false) { NPC npc = new NPC(); for (int i = 1; i < Main.maxNPCTypes; i++) { if (exact) { //Method #1 - must be exact match, allows support for different coloured slimes npc.SetDefaults(name); if (npc.name == name) return i; } else { //Method #2 - allows impartial matching name = name.ToLower(); npc.SetDefaults(i); if (npc.name.ToLower().StartsWith(name)) return i; } } return -1; } public static int GetItemID(string name) { Item item = new Item(); name = name.ToLower(); for (int i = 1; i < Main.maxItemTypes; i++) { item.SetDefaults(i); if (item.name.ToLower().StartsWith(name)) return i; } return -1; } public static bool CheckSpawn(int x, int y) { Vector2 tile = new Vector2(x, y); Vector2 spawn = new Vector2(Main.spawnTileX, Main.spawnTileY); var distance = Vector2.Distance(spawn, tile); if (distance > ConfigurationManager.SpawnProtectRadius) return false; else return true; } public static void RevertKillTile(TSPlayer player) { Tile[] tiles = new Tile[player.TilesDestroyed.Count]; player.TilesDestroyed.Values.CopyTo(tiles, 0); Vector2[] positions = new Vector2[player.TilesDestroyed.Count]; player.TilesDestroyed.Keys.CopyTo(positions, 0); for (int i = (player.TilesDestroyed.Count - 1); i >= 0; i--) { Main.tile[(int)positions[i].X, (int)positions[i].Y] = tiles[i]; NetMessage.SendData(17, -1, -1, "", 1, positions[i].X, positions[i].Y, (float)0); } } public static bool HackedHealth(int ply) { return (Main.player[ply].statManaMax > 200) || (Main.player[ply].statMana > 200) || (Main.player[ply].statLifeMax > 400) || (Main.player[ply].statLife > 400); } static readonly Dictionary MsgNames = new Dictionary() { {1, "Connect Request"}, {2, "Disconnect"}, {3, "Continue Connecting"}, {4, "Player Info"}, {5, "Player Slot"}, {6, "Continue Connecting (2)"}, {7, "World Info"}, {8, "Tile Get Section"}, {9, "Status"}, {10, "Tile Send Section"}, {11, "Tile Frame Section"}, {12, "Player Spawn"}, {13, "Player Update"}, {14, "Player Active"}, {15, "Sync Players"}, {16, "Player HP"}, {17, "Tile"}, {18, "Time Set"}, {19, "Door Use"}, {20, "Tile Send Square"}, {21, "Item Drop"}, {22, "Item Owner"}, {23, "Npc Update"}, {24, "Npc Item Strike"}, {25, "Chat Text"}, {26, "Player Damage"}, {27, "Projectile New"}, {28, "Npc Strike"}, {29, "Projectile Destroy"}, {30, "Toggle PVP"}, {31, "Chest Get Contents"}, {32, "Chest Item"}, {33, "Chest Open"}, {34, "Tile Kill"}, {35, "Effect Heal"}, {36, "Zones"}, {37, "Password Requied"}, {38, "Password Send"}, {39, "Item Unown"}, {40, "Npc Talk"}, {41, "Player Animation"}, {42, "Player Mana"}, {43, "Effect Mana"}, {44, "Player Kill Me"}, {45, "Player Team"}, {46, "Sign Read"}, {47, "Sign New"}, {48, "Liquid Set"}, {49, "Player Spawn Self"}, }; } }