Merge pull request #545 from NyxStudios/1.2_terraria
Update to Terraria Protocol 1.2
This commit is contained in:
commit
33e0627f9e
19 changed files with 994 additions and 674 deletions
|
|
@ -24,7 +24,6 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using TShockAPI.PluginUpdater;
|
||||
using Terraria;
|
||||
using TShockAPI.DB;
|
||||
|
||||
|
|
@ -186,7 +185,9 @@ namespace TShockAPI
|
|||
add(Permissions.causeevents, Star, "star");
|
||||
add(Permissions.causeevents, Fullmoon, "fullmoon");
|
||||
add(Permissions.causeevents, Bloodmoon, "bloodmoon");
|
||||
add(Permissions.causeevents, Eclipse, "eclipse");
|
||||
add(Permissions.causeevents, Invade, "invade");
|
||||
add(Permissions.causeevents, Rain, "rain");
|
||||
add(Permissions.spawnboss, Eater, "eater");
|
||||
add(Permissions.spawnboss, Eye, "eye");
|
||||
add(Permissions.spawnboss, King, "king");
|
||||
|
|
@ -483,7 +484,7 @@ namespace TShockAPI
|
|||
if (TShock.RememberedPos.GetLeavePos(args.Player.Name, args.Player.IP) != Vector2.Zero)
|
||||
{
|
||||
Vector2 pos = TShock.RememberedPos.GetLeavePos(args.Player.Name, args.Player.IP);
|
||||
args.Player.Teleport((int)pos.X, (int)pos.Y + 3);
|
||||
args.Player.Teleport((int)pos.X*16, (int)pos.Y *16 + 48);
|
||||
}
|
||||
args.Player.LoginHarassed = false;
|
||||
|
||||
|
|
@ -1259,7 +1260,6 @@ namespace TShockAPI
|
|||
{
|
||||
args.Player.SendInfoMessage("Starting plugin update process:");
|
||||
args.Player.SendInfoMessage("This may take a while, do not turn off the server!");
|
||||
new PluginUpdaterThread(args.Player);
|
||||
}
|
||||
|
||||
private static void ManageRest(CommandArgs args)
|
||||
|
|
@ -1354,6 +1354,12 @@ namespace TShockAPI
|
|||
TShock.Utils.Broadcast(string.Format("{0} turned on the blood moon.", args.Player.Name), Color.Green);
|
||||
}
|
||||
|
||||
private static void Eclipse(CommandArgs args)
|
||||
{
|
||||
TSPlayer.Server.SetEclipse(true);
|
||||
TShock.Utils.Broadcast(string.Format("{0} has forced an Eclipse!", args.Player.Name), Color.Green);
|
||||
}
|
||||
|
||||
private static void Invade(CommandArgs args)
|
||||
{
|
||||
if (Main.invasionSize <= 0)
|
||||
|
|
@ -1635,7 +1641,7 @@ namespace TShockAPI
|
|||
|
||||
private static void Spawn(CommandArgs args)
|
||||
{
|
||||
if (args.Player.Teleport(Main.spawnTileX, Main.spawnTileY))
|
||||
if (args.Player.Teleport(Main.spawnTileX*16, Main.spawnTileY*16))
|
||||
args.Player.SendSuccessMessage("Teleported to the map's spawnpoint.");
|
||||
}
|
||||
|
||||
|
|
@ -1643,33 +1649,53 @@ namespace TShockAPI
|
|||
{
|
||||
if (args.Parameters.Count < 1)
|
||||
{
|
||||
args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /tp <player> ");
|
||||
args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /tp <player>");
|
||||
args.Player.SendErrorMessage(" /tp <x> <y>");
|
||||
return;
|
||||
}
|
||||
|
||||
string plStr = String.Join(" ", args.Parameters);
|
||||
var players = TShock.Utils.FindPlayer(plStr);
|
||||
if (players.Count == 0)
|
||||
args.Player.SendErrorMessage("Invalid player!");
|
||||
else if (players.Count > 1)
|
||||
TShock.Utils.SendMultipleMatchError(args.Player, players.Select(p => p.Name));
|
||||
else if (!players[0].TPAllow && !args.Player.Group.HasPermission(Permissions.tpall))
|
||||
if(args.Parameters.Count == 2)
|
||||
{
|
||||
var plr = players[0];
|
||||
args.Player.SendErrorMessage(plr.Name + " has prevented users from teleporting to them.");
|
||||
plr.SendInfoMessage(args.Player.Name + " attempted to teleport to you.");
|
||||
float x, y;
|
||||
if (float.TryParse(args.Parameters[0], out x) && float.TryParse(args.Parameters[1], out y))
|
||||
{
|
||||
args.Player.Teleport(x, y);
|
||||
args.Player.SendSuccessMessage("Teleported!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var plr = players[0];
|
||||
if (args.Player.Teleport(plr.TileX, plr.TileY + 3))
|
||||
string plStr = String.Join(" ", args.Parameters);
|
||||
var players = TShock.Utils.FindPlayer(plStr);
|
||||
if (players.Count == 0)
|
||||
{
|
||||
args.Player.SendSuccessMessage(string.Format("Teleported to {0}.", plr.Name));
|
||||
if (!args.Player.Group.HasPermission(Permissions.tphide))
|
||||
plr.SendInfoMessage(args.Player.Name + " teleported to you.");
|
||||
args.Player.SendErrorMessage("Invalid user name.");
|
||||
args.Player.SendErrorMessage("Proper syntax: /tp <player>");
|
||||
args.Player.SendErrorMessage(" /tp <x> <y>");
|
||||
}
|
||||
|
||||
else if (players.Count > 1)
|
||||
TShock.Utils.SendMultipleMatchError(args.Player, players.Select(p => p.Name));
|
||||
else if (!players[0].TPAllow && !args.Player.Group.HasPermission(Permissions.tpall))
|
||||
{
|
||||
var plr = players[0];
|
||||
args.Player.SendErrorMessage(plr.Name + " has prevented users from teleporting to them.");
|
||||
plr.SendInfoMessage(args.Player.Name + " attempted to teleport to you.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var plr = players[0];
|
||||
if (args.Player.Teleport(plr.TileX * 16, plr.TileY * 16 + 48))
|
||||
{
|
||||
args.Player.SendSuccessMessage(string.Format("Teleported to {0}.", plr.Name));
|
||||
if (!args.Player.Group.HasPermission(Permissions.tphide))
|
||||
plr.SendInfoMessage(args.Player.Name + " teleported to you.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static void TPHere(CommandArgs args)
|
||||
{
|
||||
|
|
@ -1688,7 +1714,7 @@ namespace TShockAPI
|
|||
{
|
||||
if (Main.player[i].active && (Main.player[i] != args.TPlayer))
|
||||
{
|
||||
if (TShock.Players[i].Teleport(args.Player.TileX, args.Player.TileY + 3))
|
||||
if (TShock.Players[i].Teleport(args.Player.TileX*16, args.Player.TileY*16 + 48))
|
||||
TShock.Players[i].SendSuccessMessage(string.Format("You were teleported to {0}.", args.Player.Name) + ".");
|
||||
}
|
||||
}
|
||||
|
|
@ -1707,7 +1733,7 @@ namespace TShockAPI
|
|||
else
|
||||
{
|
||||
var plr = players[0];
|
||||
if (plr.Teleport(args.Player.TileX, args.Player.TileY + 3))
|
||||
if (plr.Teleport(args.Player.TileX*16, args.Player.TileY*16 + 48))
|
||||
{
|
||||
plr.SendInfoMessage(string.Format("You were teleported to {0}.", args.Player.Name));
|
||||
args.Player.SendSuccessMessage(string.Format("You brought {0} here.", plr.Name));
|
||||
|
|
@ -1851,7 +1877,7 @@ namespace TShockAPI
|
|||
var plr = foundplr[0];
|
||||
if (warp.WarpPos != Vector2.Zero)
|
||||
{
|
||||
if (plr.Teleport((int)warp.WarpPos.X, (int)warp.WarpPos.Y + 3))
|
||||
if (plr.Teleport((int)warp.WarpPos.X*16, (int)warp.WarpPos.Y*16 + 48))
|
||||
{
|
||||
plr.SendSuccessMessage(string.Format("{0} warped you to {1}.", args.Player.Name, warpName));
|
||||
args.Player.SendSuccessMessage(string.Format("You warped {0} to {1}.", plr.Name, warpName));
|
||||
|
|
@ -1869,7 +1895,7 @@ namespace TShockAPI
|
|||
var warp = TShock.Warps.FindWarp(warpName);
|
||||
if (warp.WarpPos != Vector2.Zero)
|
||||
{
|
||||
if (args.Player.Teleport((int)warp.WarpPos.X, (int)warp.WarpPos.Y + 3))
|
||||
if (args.Player.Teleport((int)warp.WarpPos.X*16, (int)warp.WarpPos.Y*16 + 48))
|
||||
args.Player.SendSuccessMessage("Warped to " + warpName + ".");
|
||||
}
|
||||
else
|
||||
|
|
@ -2484,7 +2510,27 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
|
||||
//TODO: Come back here
|
||||
private static void Rain(CommandArgs args)
|
||||
{
|
||||
if (args.Parameters.Count != 1)
|
||||
{
|
||||
args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /rain <stop/start>");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args.Parameters[0])
|
||||
{
|
||||
case "start":
|
||||
Main.StartRain();
|
||||
TSPlayer.All.SendInfoMessage(string.Format("{0} caused it to rain.", args.Player.Name));
|
||||
break;
|
||||
case "stop":
|
||||
Main.StopRain();
|
||||
TSPlayer.All.SendInfoMessage(string.Format("{0} ended the downpour.", args.Player.Name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void Slap(CommandArgs args)
|
||||
{
|
||||
|
|
@ -2900,13 +2946,13 @@ namespace TShockAPI
|
|||
// worth the effort as chances are very low that overwriting the wire for a few
|
||||
// nanoseconds will cause much trouble.
|
||||
Tile tile = Main.tile[boundaryPoint.X, boundaryPoint.Y];
|
||||
bool oldWireState = tile.wire;
|
||||
tile.wire = true;
|
||||
bool oldWireState = tile.wire();
|
||||
tile.wire(true);
|
||||
|
||||
try {
|
||||
args.Player.SendTileSquare(boundaryPoint.X, boundaryPoint.Y, 1);
|
||||
} finally {
|
||||
tile.wire = oldWireState;
|
||||
tile.wire(oldWireState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3027,7 +3073,7 @@ namespace TShockAPI
|
|||
break;
|
||||
}
|
||||
|
||||
args.Player.Teleport(region.Area.Center.X, region.Area.Center.Y + 3);
|
||||
args.Player.Teleport(region.Area.Center.X*16, region.Area.Center.Y*16 + 48);
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
@ -3703,12 +3749,7 @@ namespace TShockAPI
|
|||
playerToHeal = args.Player;
|
||||
}
|
||||
|
||||
Item heart = TShock.Utils.GetItemById(58);
|
||||
Item star = TShock.Utils.GetItemById(184);
|
||||
for (int i = 0; i < 20; i++)
|
||||
playerToHeal.GiveItem(heart.type, heart.name, heart.width, heart.height, heart.maxStack);
|
||||
for (int i = 0; i < 10; i++)
|
||||
playerToHeal.GiveItem(star.type, star.name, star.width, star.height, star.maxStack);
|
||||
playerToHeal.Heal();
|
||||
if (playerToHeal == args.Player)
|
||||
{
|
||||
args.Player.SendSuccessMessage("You just got healed!");
|
||||
|
|
@ -3829,7 +3870,7 @@ namespace TShockAPI
|
|||
case "tree":
|
||||
for (int i = x - 1; i < x + 2; i++)
|
||||
{
|
||||
Main.tile[i, y].active = true;
|
||||
Main.tile[i, y].active(true);
|
||||
Main.tile[i, y].type = 2;
|
||||
Main.tile[i, y].wall = 0;
|
||||
}
|
||||
|
|
@ -3840,20 +3881,20 @@ namespace TShockAPI
|
|||
case "epictree":
|
||||
for (int i = x - 1; i < x + 2; i++)
|
||||
{
|
||||
Main.tile[i, y].active = true;
|
||||
Main.tile[i, y].active(true);
|
||||
Main.tile[i, y].type = 2;
|
||||
Main.tile[i, y].wall = 0;
|
||||
}
|
||||
Main.tile[x, y - 1].wall = 0;
|
||||
Main.tile[x, y - 1].liquid = 0;
|
||||
Main.tile[x, y - 1].active = true;
|
||||
Main.tile[x, y - 1].active(true);
|
||||
WorldGen.GrowEpicTree(x, y);
|
||||
name = "Epic Tree";
|
||||
break;
|
||||
case "mushroom":
|
||||
for (int i = x - 1; i < x + 2; i++)
|
||||
{
|
||||
Main.tile[i, y].active = true;
|
||||
Main.tile[i, y].active(true);
|
||||
Main.tile[i, y].type = 70;
|
||||
Main.tile[i, y].wall = 0;
|
||||
}
|
||||
|
|
@ -3867,7 +3908,7 @@ namespace TShockAPI
|
|||
name = "Cactus";
|
||||
break;
|
||||
case "herb":
|
||||
Main.tile[x, y].active = true;
|
||||
Main.tile[x, y].active(true);
|
||||
Main.tile[x, y].frameX = 36;
|
||||
Main.tile[x, y].type = 83;
|
||||
WorldGen.GrowAlch(x, y);
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ namespace TShockAPI.DB
|
|||
{
|
||||
try
|
||||
{
|
||||
return database.Query("INSERT INTO Bans (IP, Name, Reason, BanningUser, Date, Expiration) VALUES (@0, @1, @2, @3, @4, @5);", ip, name, reason, banner, DateTime.Now.ToString("G"), expiration) != 0;
|
||||
return database.Query("INSERT INTO Bans (IP, Name, Reason, BanningUser, Date, Expiration) VALUES (@0, @1, @2, @3, @4, @5);", ip, name, reason, banner, DateTime.UtcNow.ToString("s"), expiration) != 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/
|
||||
|
||||
using System;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Data;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
|
@ -38,6 +39,7 @@ namespace TShockAPI.DB
|
|||
new SqlColumn("Username", MySqlDbType.VarChar, 32) {Unique = true},
|
||||
new SqlColumn("Password", MySqlDbType.VarChar, 128),
|
||||
new SqlColumn("Usergroup", MySqlDbType.Text),
|
||||
new SqlColumn("Registered", MySqlDbType.Text),
|
||||
new SqlColumn("LastAccessed", MySqlDbType.Text),
|
||||
new SqlColumn("KnownIPs", MySqlDbType.Text)
|
||||
);
|
||||
|
|
@ -60,8 +62,8 @@ namespace TShockAPI.DB
|
|||
int ret;
|
||||
try
|
||||
{
|
||||
ret = database.Query("INSERT INTO Users (Username, Password, UserGroup) VALUES (@0, @1, @2);", user.Name,
|
||||
TShock.Utils.HashPassword(user.Password), user.Group);
|
||||
ret = database.Query("INSERT INTO Users (Username, Password, UserGroup, Registered) VALUES (@0, @1, @2, @3);", user.Name,
|
||||
TShock.Utils.HashPassword(user.Password), user.Group, DateTime.UtcNow.ToString("s"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -146,7 +148,7 @@ namespace TShockAPI.DB
|
|||
{
|
||||
try
|
||||
{
|
||||
if (database.Query("UPDATE Users SET LastAccessed = @0, KnownIps = @1 WHERE Username = @2;", DateTime.Now.ToString("G"), user.KnownIps, user.Name) == 0)
|
||||
if (database.Query("UPDATE Users SET LastAccessed = @0, KnownIps = @1 WHERE Username = @2;", DateTime.UtcNow.ToString("s"), user.KnownIps, user.Name) == 0)
|
||||
throw new UserNotExistException(user.Name);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -268,6 +270,7 @@ namespace TShockAPI.DB
|
|||
user.Group = result.Get<string>("Usergroup");
|
||||
user.Password = result.Get<string>("Password");
|
||||
user.Name = result.Get<string>("Username");
|
||||
user.Registered = result.Get<string>("Registered");
|
||||
user.LastAccessed = result.Get<string>("LastAccessed");
|
||||
user.KnownIps = result.Get<string>("KnownIps");
|
||||
return user;
|
||||
|
|
@ -280,14 +283,16 @@ namespace TShockAPI.DB
|
|||
public string Name { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string Group { get; set; }
|
||||
public string Registered { get; set; }
|
||||
public string LastAccessed { get; set; }
|
||||
public string KnownIps { get; set; }
|
||||
|
||||
public User(string name, string pass, string group, string last, string known)
|
||||
public User(string name, string pass, string group, string registered, string last, string known)
|
||||
{
|
||||
Name = name;
|
||||
Password = pass;
|
||||
Group = group;
|
||||
Registered = registered;
|
||||
LastAccessed = last;
|
||||
KnownIps = known;
|
||||
}
|
||||
|
|
@ -297,6 +302,7 @@ namespace TShockAPI.DB
|
|||
Name = "";
|
||||
Password = "";
|
||||
Group = "";
|
||||
Registered = "";
|
||||
LastAccessed = "";
|
||||
KnownIps = "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,17 +78,17 @@ namespace TShockAPI
|
|||
/// <summary>
|
||||
/// The Tile ID being edited.
|
||||
/// </summary>
|
||||
public byte Type { get; set; }
|
||||
public byte EditData { get; set; }
|
||||
/// <summary>
|
||||
/// The EditType.
|
||||
/// (KillTile = 0, PlaceTile = 1, KillWall = 2, PlaceWall = 3, KillTileNoItem = 4, PlaceWire = 5, KillWire = 6)
|
||||
/// </summary>
|
||||
public byte EditType { get; set; }
|
||||
public EditAction Action { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Did the tile get destroyed successfully.
|
||||
/// </summary>
|
||||
public bool Fail { get; set; }
|
||||
public EditType editDetail { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used when a tile is placed to denote a subtype of tile. (e.g. for tile id 21: Chest = 0, Gold Chest = 1)
|
||||
|
|
@ -100,7 +100,7 @@ namespace TShockAPI
|
|||
/// TileEdit - called when a tile is placed or destroyed
|
||||
/// </summary>
|
||||
public static HandlerList<TileEditEventArgs> TileEdit;
|
||||
private static bool OnTileEdit(TSPlayer ply, int x, int y, byte type, byte editType, bool fail, byte style)
|
||||
private static bool OnTileEdit(TSPlayer ply, int x, int y, EditAction action, EditType editDetail, byte editData, byte style)
|
||||
{
|
||||
if (TileEdit == null)
|
||||
return false;
|
||||
|
|
@ -110,9 +110,9 @@ namespace TShockAPI
|
|||
Player = ply,
|
||||
X = x,
|
||||
Y = y,
|
||||
Type = type,
|
||||
EditType = editType,
|
||||
Fail = fail,
|
||||
Action = action,
|
||||
EditData = editData,
|
||||
editDetail = editDetail,
|
||||
Style = style
|
||||
};
|
||||
TileEdit.Invoke(null, args);
|
||||
|
|
@ -443,13 +443,15 @@ namespace TShockAPI
|
|||
/// Velocity of the player
|
||||
/// </summary>
|
||||
public Vector2 Velocity { get; set; }
|
||||
|
||||
public byte Pulley { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// PlayerUpdate - When the player sends it's updated information to the server
|
||||
/// </summary>
|
||||
public static HandlerList<PlayerUpdateEventArgs> PlayerUpdate;
|
||||
|
||||
private static bool OnPlayerUpdate(byte player, byte control, byte item, Vector2 position, Vector2 velocity)
|
||||
private static bool OnPlayerUpdate(byte player, byte control, byte item, Vector2 position, Vector2 velocity, byte pulley)
|
||||
{
|
||||
if (PlayerUpdate == null)
|
||||
return false;
|
||||
|
|
@ -461,16 +463,17 @@ namespace TShockAPI
|
|||
Item = item,
|
||||
Position = position,
|
||||
Velocity = velocity,
|
||||
Pulley = pulley
|
||||
};
|
||||
PlayerUpdate.Invoke(null, args);
|
||||
return args.Handled;
|
||||
}
|
||||
public static bool TSCheckNoclip(Vector2 Position, int Width, int Height)
|
||||
{
|
||||
int num = (int)(Position.X / 16f) - 1;
|
||||
int num2 = (int)((Position.X + (float)Width) / 16f) + 2;
|
||||
int num3 = (int)(Position.Y / 16f) - 1;
|
||||
int num4 = (int)((Position.Y + (float)Height) / 16f) + 2;
|
||||
int num = (int)(Position.X / 16f);
|
||||
int num2 = (int)((Position.X + (float)Width) / 16f);
|
||||
int num3 = (int)(Position.Y / 16f);
|
||||
int num4 = (int)((Position.Y + (float)Height) / 16f);
|
||||
if (num < 0)
|
||||
{
|
||||
num = 0;
|
||||
|
|
@ -499,15 +502,17 @@ namespace TShockAPI
|
|||
{
|
||||
for (int j = num3; j < num4; j++)
|
||||
{
|
||||
if (Main.tile[i, j] != null && Main.tile[i, j].active && Main.tileSolid[(int)Main.tile[i, j].type] && !Main.tileSolidTop[(int)Main.tile[i, j].type] &&(((int)Main.tile[i,j].type !=53) && ((int)Main.tile[i,j].type !=112) && ((int)Main.tile[i,j].type !=116) && ((int)Main.tile[i,j].type !=123)) && !Main.tile[i,j].lava)
|
||||
if (Main.tile[i, j] == null || !Main.tile[i, j].active() || !Main.tileSolid[(int) Main.tile[i, j].type] || Main.tileSolidTop[(int) Main.tile[i, j].type] || (((int) Main.tile[i, j].type == 53) || ((int) Main.tile[i, j].type == 112) || ((int) Main.tile[i, j].type == 116) || ((int) Main.tile[i, j].type == 123)) || Main.tile[i, j].lava() || ((Main.tile[i, j].tileHeader2 & 16) == 16) || ((Main.tile[i, j].tileHeader2 & 32) == 32) || ((Main.tile[i, j].tileHeader & 32) == 32) || Main.tile[i, j].honey() || !Main.tileSolid[(int) Main.tile[i + 1, j].type] || !Main.tileSolid[(int) Main.tile[i - 1, j].type] || !Main.tileSolid[(int) Main.tile[i, j + 1].type] || !Main.tileSolid[(int) Main.tile[i, j - 1].type] || !Main.tileSolid[(int) Main.tile[i - 1, j - 1].type] || !Main.tileSolid[(int) Main.tile[i - 1, j + 1].type] || !Main.tileSolid[(int) Main.tile[i + 1, j - 1].type] || !Main.tileSolid[(int) Main.tile[i + 1, j + 1].type] || Main.tileSand[(int) Main.tile[i, j].type])
|
||||
{
|
||||
Vector2 vector;
|
||||
vector.X = (float)(i * 16);
|
||||
vector.Y = (float)(j * 16);
|
||||
if (Position.X + (float)Width > vector.X && Position.X < vector.X + 16f && Position.Y + (float)Height > vector.Y && Position.Y < vector.Y + 16f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector2 vector;
|
||||
vector.X = (float) (i*16);
|
||||
vector.Y = (float) (j*16);
|
||||
if (Position.X + (float) Width > vector.X && Position.X < vector.X + 16f && Position.Y + (float) Height > vector.Y && Position.Y < vector.Y + 16f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -628,20 +633,20 @@ namespace TShockAPI
|
|||
/// </summary>
|
||||
public int TileY { get; set; }
|
||||
/// <summary>
|
||||
/// ???
|
||||
/// Amount of liquid
|
||||
/// </summary>
|
||||
public byte Liquid { get; set;}
|
||||
public byte Amount { get; set;}
|
||||
/// <summary>
|
||||
/// True if lava
|
||||
/// Type of Liquid: 0=water, 1=lave, 2=honey
|
||||
/// </summary>
|
||||
public bool Lava { get; set; }
|
||||
public byte Type { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// LiquidSet - When ever a liquid is set
|
||||
/// </summary>
|
||||
public static HandlerList<LiquidSetEventArgs> LiquidSet;
|
||||
|
||||
private static bool OnLiquidSet(int tilex, int tiley, byte liquid, bool lava)
|
||||
private static bool OnLiquidSet(int tilex, int tiley, byte amount, byte type)
|
||||
{
|
||||
if (LiquidSet == null)
|
||||
return false;
|
||||
|
|
@ -650,8 +655,8 @@ namespace TShockAPI
|
|||
{
|
||||
TileX = tilex,
|
||||
TileY = tiley,
|
||||
Liquid = liquid,
|
||||
Lava = lava,
|
||||
Amount = amount,
|
||||
Type = type,
|
||||
};
|
||||
LiquidSet.Invoke(null, args);
|
||||
return args.Handled;
|
||||
|
|
@ -919,6 +924,10 @@ namespace TShockAPI
|
|||
/// </summary>
|
||||
public byte Prefix { get; set; }
|
||||
/// <summary>
|
||||
/// No Delay on pickup
|
||||
/// </summary>
|
||||
public bool NoDelay { get; set; }
|
||||
/// <summary>
|
||||
/// Item type
|
||||
/// </summary>
|
||||
public short Type { get; set; }
|
||||
|
|
@ -928,7 +937,7 @@ namespace TShockAPI
|
|||
/// </summary>
|
||||
public static HandlerList<ItemDropEventArgs> ItemDrop;
|
||||
|
||||
private static bool OnItemDrop(short id, Vector2 pos, Vector2 vel, byte stacks, byte prefix, short type)
|
||||
private static bool OnItemDrop(short id, Vector2 pos, Vector2 vel, byte stacks, byte prefix, bool noDelay, short type)
|
||||
{
|
||||
if (ItemDrop == null)
|
||||
return false;
|
||||
|
|
@ -940,6 +949,7 @@ namespace TShockAPI
|
|||
Velocity = vel,
|
||||
Stacks = stacks,
|
||||
Prefix = prefix,
|
||||
NoDelay = noDelay,
|
||||
Type = type,
|
||||
};
|
||||
ItemDrop.Invoke(null, args);
|
||||
|
|
@ -1125,6 +1135,53 @@ namespace TShockAPI
|
|||
return args.Handled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For use with a NPCStrike event
|
||||
/// </summary>
|
||||
public class TeleportEventArgs : HandledEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// ???
|
||||
/// </summary>
|
||||
public Int16 ID { get; set; }
|
||||
/// <summary>
|
||||
/// Flag is a bit field
|
||||
/// if the first bit is set -> 0 = player, 1 = NPC
|
||||
/// if the second bit is set, ignore this packet
|
||||
/// if the third bit is set, style +1
|
||||
/// if the fourth bit is set, style +1
|
||||
/// </summary>
|
||||
public byte Flag { get; set; }
|
||||
/// <summary>
|
||||
/// X Location
|
||||
/// </summary>
|
||||
public float X { get; set; }
|
||||
/// <summary>
|
||||
/// Y Location
|
||||
/// </summary>
|
||||
public float Y { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// NPCStrike - Called when an NPC is attacked
|
||||
/// </summary>
|
||||
public static HandlerList<TeleportEventArgs> Teleport;
|
||||
|
||||
private static bool OnTeleport(Int16 id, byte f, float x, float y)
|
||||
{
|
||||
if (Teleport == null)
|
||||
return false;
|
||||
|
||||
var args = new TeleportEventArgs
|
||||
{
|
||||
ID = id,
|
||||
Flag = f,
|
||||
X = x,
|
||||
Y = y
|
||||
};
|
||||
Teleport.Invoke(null, args);
|
||||
return args.Handled;
|
||||
}
|
||||
|
||||
#endregion
|
||||
public static void InitGetDataHandler()
|
||||
{
|
||||
|
|
@ -1169,7 +1226,10 @@ namespace TShockAPI
|
|||
{PacketTypes.PasswordSend, HandlePassword},
|
||||
{PacketTypes.ContinueConnecting2, HandleConnecting},
|
||||
{PacketTypes.ProjectileDestroy, HandleProjectileKill},
|
||||
{PacketTypes.SpawnBossorInvasion, HandleSpawnBoss}
|
||||
{PacketTypes.SpawnBossorInvasion, HandleSpawnBoss},
|
||||
{PacketTypes.Teleport, HandleTeleport},
|
||||
{PacketTypes.PaintTile, HandlePaintTile},
|
||||
{PacketTypes.PaintWall, HandlePaintWall}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1251,7 +1311,7 @@ namespace TShockAPI
|
|||
if (args.Player.FirstMaxHP == 0)
|
||||
args.Player.FirstMaxHP = max;
|
||||
|
||||
if (max > TShock.Config.MaxHealth && max > args.Player.FirstMaxHP)
|
||||
if ((max > TShock.Config.MaxHealth && max > args.Player.FirstMaxHP) && !args.Player.Group.HasPermission(Permissions.ignorestathackdetection))
|
||||
{
|
||||
TShock.Utils.ForceKick(args.Player, "Hacked Client Detected.", true);
|
||||
return false;
|
||||
|
|
@ -1262,6 +1322,11 @@ namespace TShockAPI
|
|||
args.Player.PlayerData.maxHealth = max;
|
||||
}
|
||||
|
||||
if (args.Player.Group.HasPermission(Permissions.godmode) && (cur < max))
|
||||
{
|
||||
args.Player.Heal(args.TPlayer.statLifeMax);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1330,6 +1395,7 @@ namespace TShockAPI
|
|||
private static bool HandleConnecting(GetDataHandlerArgs args)
|
||||
{
|
||||
var user = TShock.Users.GetUserByName(args.Player.Name);
|
||||
|
||||
if (user != null && !TShock.Config.DisableLoginBeforeJoin)
|
||||
{
|
||||
args.Player.RequiresPassword = true;
|
||||
|
|
@ -1388,7 +1454,7 @@ namespace TShockAPI
|
|||
return true;
|
||||
}
|
||||
}
|
||||
args.Player.LoginFailsBySsi = false;
|
||||
args.Player.LoginFailsBySsi = false;
|
||||
|
||||
if (group.HasPermission(Permissions.ignorestackhackdetection))
|
||||
args.Player.IgnoreActionsForCheating = "none";
|
||||
|
|
@ -1543,10 +1609,10 @@ namespace TShockAPI
|
|||
continue;
|
||||
}
|
||||
// Server now has a range check built in
|
||||
/*if (TShock.CheckRangePermission(args.Player, realx, realy))
|
||||
if (TShock.CheckRangePermission(args.Player, realx, realy))
|
||||
{
|
||||
continue;
|
||||
}*/
|
||||
}
|
||||
if ((tile.type == 128 && newtile.Type == 128) || (tile.type == 105 && newtile.Type == 105) || (tile.type == 139 && newtile.Type == 139))
|
||||
{
|
||||
if (TShock.Config.EnableInsecureTileFixes)
|
||||
|
|
@ -1673,16 +1739,39 @@ namespace TShockAPI
|
|||
return true;
|
||||
}
|
||||
|
||||
public enum EditAction
|
||||
{
|
||||
KillTile = 0,
|
||||
PlaceTile,
|
||||
KillWall,
|
||||
PlaceWall,
|
||||
KillTileNoItem,
|
||||
PlaceWire,
|
||||
KillWire,
|
||||
PoundTile
|
||||
}
|
||||
public enum EditType
|
||||
{
|
||||
Fail = 0,
|
||||
Type,
|
||||
Slope,
|
||||
}
|
||||
private static bool HandleTile(GetDataHandlerArgs args)
|
||||
{
|
||||
var type = args.Data.ReadInt8();
|
||||
EditAction action = (EditAction)args.Data.ReadInt8();
|
||||
var tileX = args.Data.ReadInt32();
|
||||
var tileY = args.Data.ReadInt32();
|
||||
var tiletype = args.Data.ReadInt8();
|
||||
var fail = tiletype == 1;
|
||||
var editData = args.Data.ReadInt8();
|
||||
EditType type = (action == EditAction.KillTile || action == EditAction.KillWall ||
|
||||
action == EditAction.KillTileNoItem)
|
||||
? EditType.Fail
|
||||
: (action == EditAction.PlaceTile || action == EditAction.PlaceWall)
|
||||
? EditType.Type
|
||||
: EditType.Slope;
|
||||
|
||||
var style = args.Data.ReadInt8();
|
||||
|
||||
if (OnTileEdit(args.Player, tileX, tileY, tiletype, type, fail, style))
|
||||
if (OnTileEdit(args.Player, tileX, tileY, action, type, editData, style))
|
||||
return true;
|
||||
if (!TShock.Utils.TilePlacementValid(tileX, tileY))
|
||||
return false;
|
||||
|
|
@ -1767,62 +1856,64 @@ namespace TShockAPI
|
|||
if (type == 0 && Main.tile[tileX, tileY].type != 127 && !Main.tileCut[Main.tile[tileX, tileY].type] && !rightClickKill.Contains(Main.tile[tileX, tileY].type))
|
||||
{
|
||||
// If the tile is an axe tile and they aren't selecting an axe, they're hacking.
|
||||
if (Main.tileAxe[Main.tile[tileX, tileY].type] && selectedItem.axe == 0)
|
||||
if (Main.tileAxe[Main.tile[tileX, tileY].type] && (selectedItem.axe == 0 && selectedItem.explosive == 0 && args.Player.RecentFuse == 0))
|
||||
{
|
||||
args.Player.SendTileSquare(tileX, tileY);
|
||||
return true;
|
||||
}
|
||||
// If the tile is a hammer tile and they aren't selecting an hammer, they're hacking.
|
||||
else if (Main.tileHammer[Main.tile[tileX, tileY].type] && selectedItem.hammer == 0)
|
||||
else if (Main.tileHammer[Main.tile[tileX, tileY].type] && (selectedItem.hammer == 0 && selectedItem.explosive == 0 && args.Player.RecentFuse == 0))
|
||||
{
|
||||
args.Player.SendTileSquare(tileX, tileY);
|
||||
return true;
|
||||
}
|
||||
// If the tile is a pickaxe tile and they aren't selecting an pickaxe, they're hacking.
|
||||
else if ((!Main.tileAxe[Main.tile[tileX, tileY].type] && !Main.tileHammer[Main.tile[tileX, tileY].type]) && selectedItem.pick == 0)
|
||||
else if ((!Main.tileAxe[Main.tile[tileX, tileY].type] && !Main.tileHammer[Main.tile[tileX, tileY].type]) && (selectedItem.pick == 0 && selectedItem.explosive == 0 && args.Player.RecentFuse == 0))
|
||||
{
|
||||
args.Player.SendTileSquare(tileX, tileY);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (type == 2)
|
||||
else if (action == EditAction.KillWall)
|
||||
{
|
||||
// If they aren't selecting an hammer, they're hacking.
|
||||
if (selectedItem.hammer == 0)
|
||||
if (selectedItem.hammer == 0 && selectedItem.explosive == 0 && args.Player.RecentFuse == 0)
|
||||
{
|
||||
args.Player.SendTileSquare(tileX, tileY);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
else if (type == 1 || type == 3)
|
||||
else if (action == EditAction.PlaceTile || action == EditAction.PlaceWall)
|
||||
{
|
||||
if (type == 1 && TShock.Config.PreventInvalidPlaceStyle && ((tiletype == 4 && style > 8) ||
|
||||
(tiletype == 13 && style > 4) || (tiletype == 15 && style > 1) || (tiletype == 21 && style > 6) ||
|
||||
(tiletype == 82 && style > 5) || (tiletype == 91 && style > 3) || (tiletype == 105 && style > 42) ||
|
||||
(tiletype == 135 && style > 3) || (tiletype == 139 && style > 12) || (tiletype == 144 && style > 2) ||
|
||||
(tiletype == 149 && style > 2)))
|
||||
if (action == EditAction.PlaceTile && TShock.Config.PreventInvalidPlaceStyle && ((editData == 4 && style > 11) ||
|
||||
(editData == 13 && style > 4) || (editData == 15 && style > 17) || (editData == 21 && style > 22) ||
|
||||
(editData == 82 && style > 5) || (editData == 91 && style > 21) || (editData == 105 && style > 49) ||
|
||||
(editData == 135 && style > 6) || (editData == 139 && style > 27) || (editData == 144 && style > 2) ||
|
||||
(editData == 149 && style > 2)))
|
||||
{
|
||||
args.Player.SendTileSquare(tileX, tileY);
|
||||
return true;
|
||||
}
|
||||
// If they aren't selecting the item which creates the tile or wall, they're hacking.
|
||||
if (tiletype != 127 && tiletype != (type == 1 ? selectedItem.createTile : selectedItem.createWall))
|
||||
if (editData != 127 && editData != (action == EditAction.PlaceTile ? selectedItem.createTile : selectedItem.createWall))
|
||||
{
|
||||
args.Player.SendTileSquare(tileX, tileY);
|
||||
return true;
|
||||
}
|
||||
if (TShock.Itembans.ItemIsBanned(selectedItem.name, args.Player) || tiletype >= (type == 1 ? Main.maxTileSets : Main.maxWallTypes))
|
||||
if (TShock.Itembans.ItemIsBanned(selectedItem.name, args.Player) || editData >= (action == EditAction.PlaceTile ? Main.maxTileSets : Main.maxWallTypes))
|
||||
{
|
||||
args.Player.SendTileSquare(tileX, tileY);
|
||||
return true;
|
||||
}
|
||||
if (type == 1 && (tiletype == 29 || tiletype == 97) && TShock.Config.ServerSideInventory && TShock.Config.DisablePiggybanksOnSSI)
|
||||
if (action == EditAction.PlaceTile && (editData == 29 || editData == 97) && TShock.Config.ServerSideInventory && TShock.Config.DisablePiggybanksOnSSI)
|
||||
{
|
||||
args.Player.SendMessage("You cannot place this tile because server side inventory is enabled.", Color.Red);
|
||||
args.Player.SendTileSquare(tileX, tileY);
|
||||
return true;
|
||||
}
|
||||
if (type == 1 && tiletype == 21)
|
||||
if (action == EditAction.PlaceTile && editData == 21)
|
||||
{
|
||||
if (TShock.Utils.MaxChests())
|
||||
{
|
||||
|
|
@ -1838,7 +1929,7 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (type == 5)
|
||||
else if (action == EditAction.PlaceWire)
|
||||
{
|
||||
// If they aren't selecting the wrench, they're hacking.
|
||||
if (args.TPlayer.inventory[args.TPlayer.selectedItem].type != 509)
|
||||
|
|
@ -1847,7 +1938,7 @@ namespace TShockAPI
|
|||
return true;
|
||||
}
|
||||
}
|
||||
else if (type == 6)
|
||||
else if (action == EditAction.KillWire)
|
||||
{
|
||||
// If they aren't selecting the wire cutter, they're hacking.
|
||||
if (args.TPlayer.inventory[args.TPlayer.selectedItem].type != 510)
|
||||
|
|
@ -1863,13 +1954,13 @@ namespace TShockAPI
|
|||
return true;
|
||||
}
|
||||
|
||||
if (TShock.CheckTilePermission(args.Player, tileX, tileY, tiletype, type))
|
||||
if (TShock.CheckTilePermission(args.Player, tileX, tileY, editData, action))
|
||||
{
|
||||
args.Player.SendTileSquare(tileX, tileY);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((tiletype == 127 || Main.tileCut[tiletype]) && (type == 0 || type == 4))
|
||||
if ((editData == 127 || Main.tileCut[editData]) && (action ==EditAction.KillTile || action == EditAction.KillTileNoItem))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1900,26 +1991,103 @@ namespace TShockAPI
|
|||
return true;
|
||||
}
|
||||
|
||||
if ( ( type == 1 || type == 3 ) && !args.Player.Group.HasPermission(Permissions.ignoreplacetiledetection))
|
||||
if ( ( action == EditAction.PlaceTile || action == EditAction.PlaceWall ) && !args.Player.Group.HasPermission(Permissions.ignoreplacetiledetection))
|
||||
{
|
||||
args.Player.TilePlaceThreshold++;
|
||||
var coords = new Vector2(tileX, tileY);
|
||||
if (!args.Player.TilesCreated.ContainsKey(coords))
|
||||
args.Player.TilesCreated.Add(coords, Main.tile[tileX, tileY].Data);
|
||||
args.Player.TilesCreated.Add(coords, Main.tile[tileX, tileY]);
|
||||
}
|
||||
|
||||
if ((type == 0 || type == 4 || type == 2) && Main.tileSolid[Main.tile[tileX, tileY].type] &&
|
||||
if ((action == EditAction.KillTile || action == EditAction.KillTileNoItem || action == EditAction.KillWall) && Main.tileSolid[Main.tile[tileX, tileY].type] &&
|
||||
!args.Player.Group.HasPermission(Permissions.ignorekilltiledetection))
|
||||
{
|
||||
args.Player.TileKillThreshold++;
|
||||
var coords = new Vector2(tileX, tileY);
|
||||
if (!args.Player.TilesDestroyed.ContainsKey(coords))
|
||||
args.Player.TilesDestroyed.Add(coords, Main.tile[tileX, tileY].Data);
|
||||
args.Player.TilesDestroyed.Add(coords, Main.tile[tileX, tileY]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For use with a PaintTile event
|
||||
/// </summary>
|
||||
public class PaintTileEventArgs : HandledEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// X Location
|
||||
/// </summary>
|
||||
public Int32 X { get; set; }
|
||||
/// <summary>
|
||||
/// Y Location
|
||||
/// </summary>
|
||||
public Int32 Y { get; set; }
|
||||
/// <summary>
|
||||
/// Type
|
||||
/// </summary>
|
||||
public byte type { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// NPCStrike - Called when an NPC is attacked
|
||||
/// </summary>
|
||||
public static HandlerList<PaintTileEventArgs> PaintTile;
|
||||
|
||||
private static bool OnPaintTile(Int32 x, Int32 y, byte t)
|
||||
{
|
||||
if (PaintTile == null)
|
||||
return false;
|
||||
|
||||
var args = new PaintTileEventArgs
|
||||
{
|
||||
X = x,
|
||||
Y = y,
|
||||
type = t
|
||||
};
|
||||
PaintTile.Invoke(null, args);
|
||||
return args.Handled;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// For use with a PaintWall event
|
||||
/// </summary>
|
||||
public class PaintWallEventArgs : HandledEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// X Location
|
||||
/// </summary>
|
||||
public Int32 X { get; set; }
|
||||
/// <summary>
|
||||
/// Y Location
|
||||
/// </summary>
|
||||
public Int32 Y { get; set; }
|
||||
/// <summary>
|
||||
/// Type
|
||||
/// </summary>
|
||||
public byte type { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Called When a wall is painted
|
||||
/// </summary>
|
||||
public static HandlerList<PaintWallEventArgs> PaintWall;
|
||||
|
||||
private static bool OnPaintWall(Int32 x, Int32 y, byte t)
|
||||
{
|
||||
if (PaintWall == null)
|
||||
return false;
|
||||
|
||||
var args = new PaintWallEventArgs
|
||||
{
|
||||
X = x,
|
||||
Y = y,
|
||||
type = t
|
||||
};
|
||||
PaintWall.Invoke(null, args);
|
||||
return args.Handled;
|
||||
}
|
||||
|
||||
private static bool HandleTogglePvp(GetDataHandlerArgs args)
|
||||
{
|
||||
byte id = args.Data.ReadInt8();
|
||||
|
|
@ -1987,13 +2155,19 @@ namespace TShockAPI
|
|||
var item = args.Data.ReadInt8();
|
||||
var pos = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle());
|
||||
var vel = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle());
|
||||
if (OnPlayerUpdate(plr, control, item, pos, vel))
|
||||
byte pulley = args.Data.ReadInt8();
|
||||
if (OnPlayerUpdate(plr, control, item, pos, vel, pulley))
|
||||
return true;
|
||||
if (item < 0 || item >= args.TPlayer.inventory.Length)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pulley > 2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.Player.LastNetPosition == Vector2.Zero)
|
||||
{
|
||||
return true;
|
||||
|
|
@ -2036,8 +2210,8 @@ namespace TShockAPI
|
|||
args.Player.SendMessage("PvP is forced! Enable PvP or else you can't do anything!",
|
||||
Color.Red);
|
||||
}
|
||||
int lastTileX = (int) (args.Player.LastNetPosition.X/16f);
|
||||
int lastTileY = (int) (args.Player.LastNetPosition.Y/16f);
|
||||
var lastTileX = args.Player.LastNetPosition.X;
|
||||
var lastTileY = args.Player.LastNetPosition.Y;
|
||||
if (!args.Player.Teleport(lastTileX, lastTileY))
|
||||
{
|
||||
args.Player.Spawn();
|
||||
|
|
@ -2056,9 +2230,9 @@ namespace TShockAPI
|
|||
TSCheckNoclip(pos, args.TPlayer.width, args.TPlayer.height) && !TShock.Config.IgnoreNoClip
|
||||
&& !args.TPlayer.tongued)
|
||||
{
|
||||
int lastTileX = (int)(args.Player.LastNetPosition.X / 16f);
|
||||
int lastTileY = (int)(args.Player.LastNetPosition.Y / 16f);
|
||||
if (!args.Player.Teleport(lastTileX, lastTileY + 3))
|
||||
var lastTileX = args.Player.LastNetPosition.X;
|
||||
var lastTileY = args.Player.LastNetPosition.Y;
|
||||
if (!args.Player.Teleport(lastTileX, lastTileY + 48))
|
||||
{
|
||||
args.Player.SendErrorMessage("You got stuck in a solid object, Sent to spawn point.");
|
||||
args.Player.Spawn();
|
||||
|
|
@ -2092,6 +2266,8 @@ namespace TShockAPI
|
|||
args.TPlayer.controlRight = false;
|
||||
args.TPlayer.controlJump = false;
|
||||
args.TPlayer.controlUseItem = false;
|
||||
args.TPlayer.pulley = pulley != 0;
|
||||
args.TPlayer.pulleyDir = pulley;
|
||||
args.TPlayer.direction = -1;
|
||||
if ((control & 1) == 1)
|
||||
{
|
||||
|
|
@ -2207,12 +2383,14 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
|
||||
// force all explosives server-side.
|
||||
// force all explosives server-side. -- DOES NOT WORK DUE TO LATENCY
|
||||
if (hasPermission && (type == 28 || type == 29 || type == 37))
|
||||
{
|
||||
args.Player.RemoveProjectile(ident, owner);
|
||||
Projectile.NewProjectile(pos.X, pos.Y, vel.X, vel.Y, type, dmg, knockback);
|
||||
return true;
|
||||
// Denotes that the player has recently set a fuse - used for cheat detection.
|
||||
args.Player.RecentFuse = 10;
|
||||
// args.Player.RemoveProjectile(ident, owner);
|
||||
// Projectile.NewProjectile(pos.X, pos.Y, vel.X, vel.Y, type, dmg, knockback);
|
||||
// return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -2291,11 +2469,12 @@ namespace TShockAPI
|
|||
{
|
||||
int tileX = args.Data.ReadInt32();
|
||||
int tileY = args.Data.ReadInt32();
|
||||
byte liquid = args.Data.ReadInt8();
|
||||
bool lava = args.Data.ReadBoolean();
|
||||
byte amount = args.Data.ReadInt8();
|
||||
byte type = args.Data.ReadInt8();
|
||||
|
||||
if (OnLiquidSet(tileX, tileY, liquid, lava))
|
||||
if (OnLiquidSet(tileX, tileY, amount, type))
|
||||
return true;
|
||||
|
||||
if (tileX < 0 || tileX >= Main.maxTilesX || tileY < 0 || tileY >= Main.maxTilesY)
|
||||
return false;
|
||||
|
||||
|
|
@ -2314,12 +2493,16 @@ namespace TShockAPI
|
|||
|
||||
if (!args.Player.Group.HasPermission(Permissions.ignoreliquidsetdetection))
|
||||
{
|
||||
args.Player.TileLiquidThreshold++;
|
||||
args.Player.TileLiquidThreshold+=amount;
|
||||
}
|
||||
if (liquid != 0)
|
||||
if (amount != 0)
|
||||
{
|
||||
int bucket = 0;
|
||||
if (args.TPlayer.inventory[args.TPlayer.selectedItem].type == 206)
|
||||
int bucket = -1;
|
||||
if (args.TPlayer.inventory[args.TPlayer.selectedItem].type == 205)
|
||||
{
|
||||
bucket = 0;
|
||||
}
|
||||
else if (args.TPlayer.inventory[args.TPlayer.selectedItem].type == 206)
|
||||
{
|
||||
bucket = 1;
|
||||
}
|
||||
|
|
@ -2327,8 +2510,12 @@ namespace TShockAPI
|
|||
{
|
||||
bucket = 2;
|
||||
}
|
||||
else if (args.TPlayer.inventory[args.TPlayer.selectedItem].type == 1128)
|
||||
{
|
||||
bucket = 3;
|
||||
}
|
||||
|
||||
if(lava && bucket != 2)
|
||||
if (type == 1 && !(bucket == 2 || bucket == 0))
|
||||
{
|
||||
args.Player.SendErrorMessage("You do not have permission to perform this action.");
|
||||
args.Player.Disable("Spreading lava without holding a lava bucket");
|
||||
|
|
@ -2336,7 +2523,7 @@ namespace TShockAPI
|
|||
return true;
|
||||
}
|
||||
|
||||
if(lava && (!args.Player.Group.HasPermission(Permissions.usebanneditem) &&
|
||||
if(type == 1 && (!args.Player.Group.HasPermission(Permissions.usebanneditem) &&
|
||||
TShock.Itembans.ItemIsBanned("Lava Bucket", args.Player)))
|
||||
{
|
||||
args.Player.SendErrorMessage("You do not have permission to perform this action.");
|
||||
|
|
@ -2345,7 +2532,7 @@ namespace TShockAPI
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!lava && bucket != 1)
|
||||
if (type == 0 && !(bucket == 1 || bucket == 0))
|
||||
{
|
||||
args.Player.SendErrorMessage("You do not have permission to perform this action.");
|
||||
args.Player.Disable("Spreading water without holding a water bucket");
|
||||
|
|
@ -2353,7 +2540,7 @@ namespace TShockAPI
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!lava && (!args.Player.Group.HasPermission(Permissions.usebanneditem) &&
|
||||
if (type == 0 && (!args.Player.Group.HasPermission(Permissions.usebanneditem) &&
|
||||
TShock.Itembans.ItemIsBanned("Water Bucket", args.Player)))
|
||||
{
|
||||
args.Player.SendErrorMessage("You do not have permission to perform this action.");
|
||||
|
|
@ -2361,6 +2548,23 @@ namespace TShockAPI
|
|||
args.Player.SendTileSquare(tileX, tileY);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == 2 &&!(bucket == 3 || bucket == 0))
|
||||
{
|
||||
args.Player.SendErrorMessage("You do not have permission to perform this action.");
|
||||
args.Player.Disable("Spreading honey without holding a honey bucket");
|
||||
args.Player.SendTileSquare(tileX, tileY);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == 2 && (!args.Player.Group.HasPermission(Permissions.usebanneditem) &&
|
||||
TShock.Itembans.ItemIsBanned("Honey Bucket", args.Player)))
|
||||
{
|
||||
args.Player.SendErrorMessage("You do not have permission to perform this action.");
|
||||
args.Player.Disable("Using banned honey bucket without permissions");
|
||||
args.Player.SendTileSquare(tileX, tileY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (TShock.CheckTilePermission(args.Player, tileX, tileY))
|
||||
|
|
@ -2631,13 +2835,14 @@ namespace TShockAPI
|
|||
var vel = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle());
|
||||
var stacks = args.Data.ReadInt8();
|
||||
var prefix = args.Data.ReadInt8();
|
||||
var noDelay = args.Data.ReadBoolean();
|
||||
var type = args.Data.ReadInt16();
|
||||
|
||||
if (OnItemDrop(id, pos, vel, stacks, prefix, type))
|
||||
if (OnItemDrop(id, pos, vel, stacks, prefix, noDelay, type))
|
||||
return true;
|
||||
|
||||
// player is attempting to crash clients
|
||||
if (type < -24 || type >= Main.maxItemTypes)
|
||||
if (type < -48 || type >= Main.maxItemTypes)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2737,6 +2942,11 @@ namespace TShockAPI
|
|||
return true;
|
||||
}
|
||||
|
||||
if (args.Player.Group.HasPermission(Permissions.godmode))
|
||||
{
|
||||
args.Player.Heal(args.TPlayer.statLifeMax);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -2939,5 +3149,96 @@ namespace TShockAPI
|
|||
TShock.Utils.SendLogs(string.Format("{0} summoned {1}", args.Player.Name, boss), Color.PaleVioletRed, args.Player);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HandlePaintTile(GetDataHandlerArgs args)
|
||||
{
|
||||
var x = args.Data.ReadInt32();
|
||||
var y = args.Data.ReadInt32();
|
||||
var t = args.Data.ReadInt8();
|
||||
|
||||
if (OnPaintTile(x, y, t))
|
||||
return true;
|
||||
|
||||
if (!args.Player.Group.HasPermission(Permissions.canpaint))
|
||||
{
|
||||
args.Player.SendTileSquare(x, y);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HandlePaintWall(GetDataHandlerArgs args)
|
||||
{
|
||||
var x = args.Data.ReadInt32();
|
||||
var y = args.Data.ReadInt32();
|
||||
var t = args.Data.ReadInt8();
|
||||
|
||||
if (OnPaintTile(x, y, t))
|
||||
return true;
|
||||
|
||||
if (!args.Player.Group.HasPermission(Permissions.canpaint))
|
||||
{
|
||||
args.Player.SendTileSquare(x, y);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
private static bool HandleTeleport(GetDataHandlerArgs args)
|
||||
{
|
||||
var flag = args.Data.ReadInt8();
|
||||
var id = args.Data.ReadInt16();
|
||||
var x = args.Data.ReadSingle();
|
||||
var y = args.Data.ReadSingle();
|
||||
|
||||
if (OnTeleport(id, flag, x, y))
|
||||
return true;
|
||||
|
||||
byte style = 0;
|
||||
var isNPC = false;
|
||||
if ((flag & 1) == 1)
|
||||
{
|
||||
isNPC = true;
|
||||
}
|
||||
|
||||
if ((flag & 2) != 2)
|
||||
{
|
||||
if ((flag & 4) == 4)
|
||||
{
|
||||
style++;
|
||||
}
|
||||
|
||||
if ((flag & 8) == 8)
|
||||
{
|
||||
style++;
|
||||
}
|
||||
|
||||
if (id > (isNPC ? 200 : 255))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Main.player[id] == null || TShock.Players[id] == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isNPC && !args.Player.Group.HasPermission(Permissions.tp))
|
||||
{
|
||||
args.Player.SendErrorMessage("You do not have permission to teleport.");
|
||||
Main.player[id].Teleport(new Vector2(Main.player[id].position.X, Main.player[id].position.Y), style);
|
||||
NetMessage.SendData(65, -1, -1, "", 0, (float)id, Main.player[id].position.X, Main.player[id].position.Y, style);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isNPC)
|
||||
{
|
||||
TShock.Players[id].Teleport(x, y, style);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,9 +31,13 @@ namespace TShockAPI.Net
|
|||
public short FrameY { get; set; }
|
||||
public byte Wall { get; set; }
|
||||
public byte Liquid { get; set; }
|
||||
public bool Lava { get; set; }
|
||||
public byte LiquidType { get; set; }
|
||||
public bool Wire { get; set; }
|
||||
|
||||
public byte HalfBrick { get; set; }
|
||||
public byte Actuator { get; set; }
|
||||
public bool Inactive { get; set; }
|
||||
public bool IsHalf { get; set; }
|
||||
public bool IsActuator { get; set; }
|
||||
public bool HasWall
|
||||
{
|
||||
get { return Wall > 0; }
|
||||
|
|
@ -57,8 +61,10 @@ namespace TShockAPI.Net
|
|||
FrameY = -1;
|
||||
Wall = 0;
|
||||
Liquid = 0;
|
||||
Lava = false;
|
||||
Wire = false;
|
||||
HalfBrick = 0;
|
||||
Actuator = 0;
|
||||
Inactive = false;
|
||||
}
|
||||
|
||||
public NetTile(Stream stream)
|
||||
|
|
@ -71,7 +77,7 @@ namespace TShockAPI.Net
|
|||
{
|
||||
var flags = TileFlags.None;
|
||||
|
||||
if (Active)
|
||||
if ((Active) && (!Inactive))
|
||||
flags |= TileFlags.Active;
|
||||
|
||||
if (HasWall)
|
||||
|
|
@ -82,6 +88,17 @@ namespace TShockAPI.Net
|
|||
|
||||
if (Wire)
|
||||
flags |= TileFlags.Wire;
|
||||
|
||||
if (IsHalf)
|
||||
flags |= TileFlags.HalfBrick;
|
||||
|
||||
if (IsActuator)
|
||||
flags |= TileFlags.Actuator;
|
||||
|
||||
if (Inactive)
|
||||
{
|
||||
flags |= TileFlags.Inactive;
|
||||
}
|
||||
|
||||
stream.WriteInt8((byte) flags);
|
||||
|
||||
|
|
@ -101,7 +118,7 @@ namespace TShockAPI.Net
|
|||
if (HasLiquid)
|
||||
{
|
||||
stream.WriteInt8(Liquid);
|
||||
stream.WriteBoolean(Lava);
|
||||
stream.WriteInt8(LiquidType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -128,11 +145,23 @@ namespace TShockAPI.Net
|
|||
if (flags.HasFlag(TileFlags.Liquid))
|
||||
{
|
||||
Liquid = stream.ReadInt8();
|
||||
Lava = stream.ReadBoolean();
|
||||
LiquidType = stream.ReadInt8();
|
||||
}
|
||||
|
||||
if (flags.HasFlag(TileFlags.Wire))
|
||||
Wire = true;
|
||||
|
||||
if (flags.HasFlag(TileFlags.HalfBrick))
|
||||
IsHalf = true;
|
||||
|
||||
if (flags.HasFlag(TileFlags.Actuator))
|
||||
IsActuator = true;
|
||||
|
||||
if (flags.HasFlag(TileFlags.Inactive))
|
||||
{
|
||||
Inactive = true;
|
||||
Active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,6 +173,9 @@ namespace TShockAPI.Net
|
|||
Lighted = 2,
|
||||
Wall = 4,
|
||||
Liquid = 8,
|
||||
Wire = 16
|
||||
Wire = 16,
|
||||
HalfBrick = 32,
|
||||
Actuator = 64,
|
||||
Inactive = 128
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ namespace TShockAPI.Net
|
|||
{
|
||||
public override PacketTypes ID
|
||||
{
|
||||
get { return PacketTypes.ProjectileNew; }
|
||||
get{ return PacketTypes.ProjectileNew; }
|
||||
}
|
||||
|
||||
public short Index { get; set; }
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ using System.Text;
|
|||
namespace TShockAPI.Net
|
||||
{
|
||||
[Flags]
|
||||
public enum WorldInfoFlag : byte
|
||||
public enum BossFlags : byte
|
||||
{
|
||||
None = 0,
|
||||
OrbSmashed = 1,
|
||||
|
|
@ -35,12 +35,25 @@ namespace TShockAPI.Net
|
|||
DownedClown = 32
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum BossFlags2 : byte
|
||||
{
|
||||
None = 0,
|
||||
DownedMechBoss1 = 1,
|
||||
DownedMechBoss2 = 2,
|
||||
DownedMechBoss3 = 4,
|
||||
DownedMechBossAny = 8,
|
||||
CloudBg = 16,
|
||||
Crimson = 32
|
||||
}
|
||||
|
||||
public class WorldInfoMsg : BaseMsg
|
||||
{
|
||||
public int Time { get; set; }
|
||||
public bool DayTime { get; set; }
|
||||
public byte MoonPhase { get; set; }
|
||||
public bool BloodMoon { get; set; }
|
||||
public bool Eclipse { get; set; }
|
||||
public int MaxTilesX { get; set; }
|
||||
public int MaxTilesY { get; set; }
|
||||
public int SpawnX { get; set; }
|
||||
|
|
@ -48,7 +61,37 @@ namespace TShockAPI.Net
|
|||
public int WorldSurface { get; set; }
|
||||
public int RockLayer { get; set; }
|
||||
public int WorldID { get; set; }
|
||||
public WorldInfoFlag WorldFlags { get; set; }
|
||||
public byte MoonType { get; set; }
|
||||
public int TreeX0 { get; set; }
|
||||
public int TreeX1 { get; set; }
|
||||
public int TreeX2 { get; set; }
|
||||
public byte TreeStyle0 { get; set; }
|
||||
public byte TreeStyle1 { get; set; }
|
||||
public byte TreeStyle2 { get; set; }
|
||||
public byte TreeStyle3 { get; set; }
|
||||
public int CaveBackX0 { get; set; }
|
||||
public int CaveBackX1 { get; set; }
|
||||
public int CaveBackX2 { get; set; }
|
||||
public byte CaveBackStyle0 { get; set; }
|
||||
public byte CaveBackStyle1 { get; set; }
|
||||
public byte CaveBackStyle2 { get; set; }
|
||||
public byte CaveBackStyle3 { get; set; }
|
||||
public byte SetBG0 { get; set; }
|
||||
public byte SetBG1 { get; set; }
|
||||
public byte SetBG2 { get; set; }
|
||||
public byte SetBG3 { get; set; }
|
||||
public byte SetBG4 { get; set; }
|
||||
public byte SetBG5 { get; set; }
|
||||
public byte SetBG6 { get; set; }
|
||||
public byte SetBG7 { get; set; }
|
||||
public byte IceBackStyle { get; set; }
|
||||
public byte JungleBackStyle { get; set; }
|
||||
public byte HellBackStyle { get; set; }
|
||||
public float WindSpeed { get; set; }
|
||||
public byte NumberOfClouds { get; set; }
|
||||
public BossFlags BossFlags { get; set; }
|
||||
public BossFlags2 BossFlags2 { get; set; }
|
||||
public float Rain { get; set; }
|
||||
public string WorldName { get; set; }
|
||||
|
||||
public override PacketTypes ID
|
||||
|
|
@ -62,6 +105,7 @@ namespace TShockAPI.Net
|
|||
stream.WriteBoolean(DayTime);
|
||||
stream.WriteInt8(MoonPhase);
|
||||
stream.WriteBoolean(BloodMoon);
|
||||
stream.WriteBoolean(Eclipse);
|
||||
stream.WriteInt32(MaxTilesX);
|
||||
stream.WriteInt32(MaxTilesY);
|
||||
stream.WriteInt32(SpawnX);
|
||||
|
|
@ -69,7 +113,37 @@ namespace TShockAPI.Net
|
|||
stream.WriteInt32(WorldSurface);
|
||||
stream.WriteInt32(RockLayer);
|
||||
stream.WriteInt32(WorldID);
|
||||
stream.WriteInt8((byte) WorldFlags);
|
||||
stream.WriteByte(MoonType);
|
||||
stream.WriteInt32(TreeX0);
|
||||
stream.WriteInt32(TreeX1);
|
||||
stream.WriteInt32(TreeX2);
|
||||
stream.WriteByte(TreeStyle0);
|
||||
stream.WriteByte(TreeStyle1);
|
||||
stream.WriteByte(TreeStyle2);
|
||||
stream.WriteByte(TreeStyle3);
|
||||
stream.WriteInt32(CaveBackX0);
|
||||
stream.WriteInt32(CaveBackX1);
|
||||
stream.WriteInt32(CaveBackX2);
|
||||
stream.WriteByte(CaveBackStyle0);
|
||||
stream.WriteByte(CaveBackStyle1);
|
||||
stream.WriteByte(CaveBackStyle2);
|
||||
stream.WriteByte(CaveBackStyle3);
|
||||
stream.WriteByte(SetBG0);
|
||||
stream.WriteByte(SetBG1);
|
||||
stream.WriteByte(SetBG2);
|
||||
stream.WriteByte(SetBG3);
|
||||
stream.WriteByte(SetBG4);
|
||||
stream.WriteByte(SetBG5);
|
||||
stream.WriteByte(SetBG6);
|
||||
stream.WriteByte(SetBG7);
|
||||
stream.WriteByte(IceBackStyle);
|
||||
stream.WriteByte(JungleBackStyle);
|
||||
stream.WriteByte(HellBackStyle);
|
||||
stream.WriteSingle(WindSpeed);
|
||||
stream.WriteByte(NumberOfClouds);
|
||||
stream.WriteInt8((byte) BossFlags);
|
||||
stream.WriteInt8((byte)BossFlags2);
|
||||
stream.WriteSingle(Rain);
|
||||
stream.WriteBytes(Encoding.UTF8.GetBytes(WorldName));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ using System.ComponentModel;
|
|||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using Hooks;
|
||||
using Terraria;
|
||||
using TerrariaApi.Server;
|
||||
|
||||
namespace TShockAPI
|
||||
{
|
||||
|
|
@ -34,6 +34,8 @@ namespace TShockAPI
|
|||
/// </summary>
|
||||
public int BytesPerUpdate { get; set; }
|
||||
|
||||
private readonly TShock plugin;
|
||||
|
||||
private PacketBuffer[] buffers = new PacketBuffer[Netplay.serverSock.Length];
|
||||
|
||||
private int[] Bytes = new int[52];
|
||||
|
|
@ -45,8 +47,9 @@ namespace TShockAPI
|
|||
Command flush;
|
||||
#endif
|
||||
|
||||
public PacketBufferer()
|
||||
public PacketBufferer(TShock p)
|
||||
{
|
||||
plugin = p;
|
||||
BytesPerUpdate = 0xFFFF;
|
||||
for (int i = 0; i < buffers.Length; i++)
|
||||
buffers[i] = new PacketBuffer();
|
||||
|
|
@ -58,9 +61,9 @@ namespace TShockAPI
|
|||
Commands.ChatCommands.Add(flush);
|
||||
#endif
|
||||
|
||||
NetHooks.SendBytes += ServerHooks_SendBytes;
|
||||
ServerHooks.SocketReset += ServerHooks_SocketReset;
|
||||
GameHooks.PostUpdate += GameHooks_Update;
|
||||
ServerApi.Hooks.NetSendBytes.Register(plugin, ServerHooks_SendBytes);
|
||||
ServerApi.Hooks.ServerSocketReset.Register(plugin, ServerHooks_SocketReset);
|
||||
ServerApi.Hooks.GamePostUpdate.Register(plugin, GameHooks_Update);
|
||||
}
|
||||
|
||||
~PacketBufferer()
|
||||
|
|
@ -82,9 +85,9 @@ namespace TShockAPI
|
|||
Commands.ChatCommands.Remove(dump);
|
||||
Commands.ChatCommands.Remove(flush);
|
||||
#endif
|
||||
NetHooks.SendBytes -= ServerHooks_SendBytes;
|
||||
ServerHooks.SocketReset -= ServerHooks_SocketReset;
|
||||
GameHooks.PostUpdate -= GameHooks_Update;
|
||||
ServerApi.Hooks.NetSendBytes.Deregister(plugin, ServerHooks_SendBytes);
|
||||
ServerApi.Hooks.ServerSocketReset.Deregister(plugin, ServerHooks_SocketReset);
|
||||
ServerApi.Hooks.GamePostUpdate.Deregister(plugin, GameHooks_Update);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +110,7 @@ namespace TShockAPI
|
|||
Compressed = new int[52];
|
||||
}
|
||||
|
||||
private void GameHooks_Update()
|
||||
private void GameHooks_Update(EventArgs args)
|
||||
{
|
||||
FlushAll();
|
||||
}
|
||||
|
|
@ -148,9 +151,9 @@ namespace TShockAPI
|
|||
}
|
||||
|
||||
|
||||
private void ServerHooks_SocketReset(ServerSock socket)
|
||||
private void ServerHooks_SocketReset(SocketResetEventArgs args)
|
||||
{
|
||||
buffers[socket.whoAmI] = new PacketBuffer();
|
||||
buffers[args.Socket.whoAmI] = new PacketBuffer();
|
||||
}
|
||||
|
||||
public bool SendBytes(ServerSock socket, byte[] buffer)
|
||||
|
|
@ -217,10 +220,10 @@ namespace TShockAPI
|
|||
return false;
|
||||
}
|
||||
|
||||
private void ServerHooks_SendBytes(ServerSock socket, byte[] buffer, int offset, int count, HandledEventArgs e)
|
||||
private void ServerHooks_SendBytes(SendBytesEventArgs args)
|
||||
{
|
||||
e.Handled = true;
|
||||
BufferBytes(socket, buffer, offset, count);
|
||||
args.Handled = true;
|
||||
BufferBytes(args.Socket, args.Buffer, args.Offset, args.Count);
|
||||
}
|
||||
|
||||
#if DEBUG_NET
|
||||
|
|
|
|||
|
|
@ -247,6 +247,9 @@ namespace TShockAPI
|
|||
|
||||
[Description("User can modify the world.")]
|
||||
public static readonly string canbuild = "tshock.world.modify";
|
||||
|
||||
[Description("User can paint tiles.")]
|
||||
public static readonly string canpaint = "tshock.world.paint";
|
||||
|
||||
// Non-grouped
|
||||
|
||||
|
|
@ -280,6 +283,9 @@ namespace TShockAPI
|
|||
[Description("User can get the server info.")]
|
||||
public static readonly string serverinfo = "tshock.info";
|
||||
|
||||
[Description("Player recovers health as damage is taken. Can be one shotted.")]
|
||||
public static readonly string godmode = "tshock.godmode";
|
||||
|
||||
/// <summary>
|
||||
/// Lists all commands associated with a given permission
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
TShock, a server mod for Terraria
|
||||
Copyright (C) 2011-2013 Nyx Studios (fka. 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Terraria;
|
||||
|
||||
namespace TShockAPI.PluginUpdater
|
||||
{
|
||||
class PluginUpdaterThread
|
||||
{
|
||||
private TSPlayer invoker;
|
||||
public PluginUpdaterThread(TSPlayer player)
|
||||
{
|
||||
invoker = player;
|
||||
PluginVersionCheck.PluginUpdate += PluginUpdate;
|
||||
HandleUpdate();
|
||||
}
|
||||
|
||||
private void HandleUpdate()
|
||||
{
|
||||
foreach(PluginContainer cont in ProgramServer.Plugins)
|
||||
new Thread(PluginVersionCheck.CheckPlugin).Start(cont.Plugin);
|
||||
}
|
||||
|
||||
private int Updates = 0;
|
||||
private void PluginUpdate(UpdateArgs args)
|
||||
{
|
||||
Updates++;
|
||||
if(args.Success && String.IsNullOrEmpty(args.Error))
|
||||
{
|
||||
invoker.SendSuccessMessage(String.Format("{0} was downloaded successfully.", args.Plugin.Name));
|
||||
}
|
||||
else if(args.Success)
|
||||
{
|
||||
invoker.SendSuccessMessage(String.Format("{0} was skipped. Reason: {1}", args.Plugin.Name, args.Error));
|
||||
}
|
||||
else
|
||||
{
|
||||
invoker.SendSuccessMessage(String.Format("{0} failed to downloaded. Error: {1}", args.Plugin.Name, args.Error));
|
||||
}
|
||||
|
||||
if(Updates >= Terraria.ProgramServer.Plugins.Count)
|
||||
{
|
||||
PluginVersionCheck.PluginUpdate -= PluginUpdate;
|
||||
|
||||
invoker.SendSuccessMessage("All plugins have been downloaded. Now copying them to the plugin folder...");
|
||||
|
||||
string folder = Path.Combine(TShock.SavePath, "UpdatedPlugins");
|
||||
string dest = Path.Combine(TShock.SavePath, "..", "ServerPlugins");
|
||||
|
||||
foreach (string dir in Directory.GetDirectories(folder, "*", System.IO.SearchOption.AllDirectories))
|
||||
{
|
||||
string new_folder = dest + dir.Substring(folder.Length);
|
||||
if (!Directory.Exists(new_folder))
|
||||
Directory.CreateDirectory(new_folder);
|
||||
}
|
||||
|
||||
foreach (string file_name in Directory.GetFiles(folder, "*.*", System.IO.SearchOption.AllDirectories))
|
||||
{
|
||||
TSPlayer.Server.SendSuccessMessage(String.Format("Copied {0}", file_name));
|
||||
File.Copy(file_name, dest + file_name.Substring(folder.Length), true);
|
||||
}
|
||||
|
||||
|
||||
Directory.Delete(folder, true);
|
||||
|
||||
invoker.SendSuccessMessage("All plugins have been processed. Restart the server to have access to the new plugins.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
TShock, a server mod for Terraria
|
||||
Copyright (C) 2011-2013 Nyx Studios (fka. 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using JsonLoader;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace TShockAPI.PluginUpdater
|
||||
{
|
||||
public class PluginVersionCheck
|
||||
{
|
||||
public delegate void PluginUpdateD(UpdateArgs e);
|
||||
public static event PluginUpdateD PluginUpdate;
|
||||
public static void OnPluginUpdate(UpdateArgs args)
|
||||
{
|
||||
if (PluginUpdate == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PluginUpdate(args);
|
||||
}
|
||||
|
||||
public static void CheckPlugin(object p)
|
||||
{
|
||||
TerrariaPlugin plugin = (TerrariaPlugin)p;
|
||||
UpdateArgs args = new UpdateArgs {Plugin = plugin, Success = true, Error = ""};
|
||||
List<string> files = new List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
if (!String.IsNullOrEmpty(plugin.UpdateURL))
|
||||
{
|
||||
var request = HttpWebRequest.Create(plugin.UpdateURL);
|
||||
VersionInfo vi;
|
||||
|
||||
request.Timeout = 5000;
|
||||
using (var response = request.GetResponse())
|
||||
{
|
||||
using (var reader = new StreamReader(response.GetResponseStream()))
|
||||
{
|
||||
vi = JsonConvert.DeserializeObject<VersionInfo>(reader.ReadToEnd());
|
||||
}
|
||||
}
|
||||
|
||||
System.Version v = System.Version.Parse((vi.version.ToString()));
|
||||
|
||||
if (!v.Equals(plugin.Version))
|
||||
{
|
||||
DownloadPackage pkg;
|
||||
request = HttpWebRequest.Create(vi.url);
|
||||
|
||||
request.Timeout = 5000;
|
||||
using (var response = request.GetResponse())
|
||||
{
|
||||
using (var reader = new StreamReader(response.GetResponseStream()))
|
||||
{
|
||||
pkg = JsonConvert.DeserializeObject<DownloadPackage>(reader.ReadToEnd());
|
||||
}
|
||||
}
|
||||
|
||||
foreach (PluginFile f in pkg.files)
|
||||
{
|
||||
using (WebClient Client = new WebClient())
|
||||
{
|
||||
string dir = Path.Combine(TShock.SavePath, "UpdatedPlugins");
|
||||
if (!Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
Client.DownloadFile(f.url,
|
||||
Path.Combine(dir, f.destination));
|
||||
|
||||
files.Add(Path.Combine(dir, f.destination));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Error = "Plugin is up to date.";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Error = "Plugin has no updater recorded.";
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
args.Success = false;
|
||||
args.Error = e.Message;
|
||||
if(files.Count > 0)
|
||||
{
|
||||
foreach(string s in files)
|
||||
{
|
||||
File.Delete(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OnPluginUpdate(args);
|
||||
}
|
||||
}
|
||||
|
||||
public class UpdateArgs
|
||||
{
|
||||
public TerrariaPlugin Plugin { get; set; }
|
||||
public bool Success { get; set; }
|
||||
public string Error { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
TShock, a server mod for Terraria
|
||||
Copyright (C) 2011-2013 Nyx Studios (fka. 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace JsonLoader
|
||||
{
|
||||
class VersionInfo
|
||||
{
|
||||
public Version version;
|
||||
public string url;
|
||||
}
|
||||
|
||||
public class Version
|
||||
{
|
||||
public int Major;
|
||||
public int Minor;
|
||||
public int Build;
|
||||
public int Revision;
|
||||
public int MajorRevision;
|
||||
public int MinorRevision;
|
||||
|
||||
public Version()
|
||||
{
|
||||
SetVersion(0,0,0,0);
|
||||
}
|
||||
|
||||
public Version(int m)
|
||||
{
|
||||
SetVersion(m, 0, 0, 0);
|
||||
}
|
||||
|
||||
public Version(int ma, int mi)
|
||||
{
|
||||
SetVersion(ma, mi, 0, 0);
|
||||
}
|
||||
|
||||
public Version(int ma, int mi, int b)
|
||||
{
|
||||
SetVersion(ma, mi, b, 0);
|
||||
}
|
||||
|
||||
public Version(int ma, int mi, int b, int r)
|
||||
{
|
||||
SetVersion(ma, mi, b, r);
|
||||
}
|
||||
|
||||
private void SetVersion(int ma, int mi, int b, int r)
|
||||
{
|
||||
Major = ma;
|
||||
Minor = mi;
|
||||
Build = b;
|
||||
Revision = r;
|
||||
}
|
||||
|
||||
public string ToString()
|
||||
{
|
||||
return String.Format("{0}.{1}.{2}.{3}", Major, Minor, Build, Revision);
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadPackage
|
||||
{
|
||||
public List<PluginFile> files;
|
||||
}
|
||||
|
||||
class PluginFile
|
||||
{
|
||||
public string url;
|
||||
public string destination = "";
|
||||
}
|
||||
}
|
||||
|
|
@ -307,7 +307,7 @@ namespace TShockAPI
|
|||
return RestMissingParam("password");
|
||||
|
||||
// NOTE: ip can be blank
|
||||
User user = new User(username, password, group, "", "");
|
||||
User user = new User(username, password, group, "", "", "");
|
||||
try
|
||||
{
|
||||
TShock.Users.AddUser(user);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ using System.Collections.Generic;
|
|||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using Terraria;
|
||||
using TerrariaApi.Server;
|
||||
|
||||
namespace TShockAPI
|
||||
{
|
||||
|
|
@ -45,7 +46,7 @@ namespace TShockAPI
|
|||
/// <summary>
|
||||
/// SaveWorld event handler which notifies users that the server may lag
|
||||
/// </summary>
|
||||
public void OnSaveWorld(bool resettime = false, HandledEventArgs e = null)
|
||||
public void OnSaveWorld(WorldSaveEventArgs args)
|
||||
{
|
||||
// Protect against internal errors causing save failures
|
||||
// These can be caused by an unexpected error such as a bad or out of date plugin
|
||||
|
|
@ -120,7 +121,7 @@ namespace TShockAPI
|
|||
{
|
||||
if (task.direct)
|
||||
{
|
||||
OnSaveWorld();
|
||||
OnSaveWorld(new WorldSaveEventArgs());
|
||||
WorldGen.realsaveWorld(task.resetTime);
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -57,16 +57,21 @@ namespace TShockAPI
|
|||
/// The number of projectiles created by the player in the last second.
|
||||
/// </summary>
|
||||
public int ProjectileThreshold { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A timer to keep track of whether or not the player has recently thrown an explosive
|
||||
// </summary>
|
||||
public int RecentFuse = 0;
|
||||
|
||||
/// <summary>
|
||||
/// A queue of tiles destroyed by the player for reverting.
|
||||
/// </summary>
|
||||
public Dictionary<Vector2, TileData> TilesDestroyed { get; protected set; }
|
||||
public Dictionary<Vector2, Tile> TilesDestroyed { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// A queue of tiles placed by the player for reverting.
|
||||
/// </summary>
|
||||
public Dictionary<Vector2, TileData> TilesCreated { get; protected set; }
|
||||
public Dictionary<Vector2, Tile> TilesCreated { get; protected set; }
|
||||
|
||||
public int FirstMaxHP { get; set; }
|
||||
|
||||
|
|
@ -390,7 +395,7 @@ namespace TShockAPI
|
|||
bool flag = false;
|
||||
if (RealPlayer)
|
||||
{
|
||||
for (int i = 0; i < 40; i++) //41 is trash can, 42-45 is coins, 46-49 is ammo
|
||||
for (int i = 0; i < 50; i++) //51 is trash can, 52-55 is coins, 56-59 is ammo
|
||||
{
|
||||
if (TPlayer.inventory[i] == null || !TPlayer.inventory[i].active || TPlayer.inventory[i].name == "")
|
||||
{
|
||||
|
|
@ -405,8 +410,8 @@ namespace TShockAPI
|
|||
|
||||
public TSPlayer(int index)
|
||||
{
|
||||
TilesDestroyed = new Dictionary<Vector2, TileData>();
|
||||
TilesCreated = new Dictionary<Vector2, TileData>();
|
||||
TilesDestroyed = new Dictionary<Vector2, Tile>();
|
||||
TilesCreated = new Dictionary<Vector2, Tile>();
|
||||
Index = index;
|
||||
Group = Group.DefaultGroup;
|
||||
IceTiles = new List<Point>();
|
||||
|
|
@ -415,8 +420,8 @@ namespace TShockAPI
|
|||
|
||||
protected TSPlayer(String playerName)
|
||||
{
|
||||
TilesDestroyed = new Dictionary<Vector2, TileData>();
|
||||
TilesCreated = new Dictionary<Vector2, TileData>();
|
||||
TilesDestroyed = new Dictionary<Vector2, Tile>();
|
||||
TilesCreated = new Dictionary<Vector2, Tile>();
|
||||
Index = -1;
|
||||
FakePlayer = new Player {name = playerName, whoAmi = -1};
|
||||
Group = Group.DefaultGroup;
|
||||
|
|
@ -457,12 +462,47 @@ namespace TShockAPI
|
|||
//Sending a fake world id causes the client to not be able to find a stored spawnx/y.
|
||||
//This fixes the bed spawn point bug. With a fake world id it wont be able to find the bed spawn.
|
||||
WorldID = !fakeid ? Main.worldID : -1,
|
||||
WorldFlags = (WorldGen.shadowOrbSmashed ? WorldInfoFlag.OrbSmashed : WorldInfoFlag.None) |
|
||||
(NPC.downedBoss1 ? WorldInfoFlag.DownedBoss1 : WorldInfoFlag.None) |
|
||||
(NPC.downedBoss2 ? WorldInfoFlag.DownedBoss2 : WorldInfoFlag.None) |
|
||||
(NPC.downedBoss3 ? WorldInfoFlag.DownedBoss3 : WorldInfoFlag.None) |
|
||||
(Main.hardMode ? WorldInfoFlag.HardMode : WorldInfoFlag.None) |
|
||||
(NPC.downedClown ? WorldInfoFlag.DownedClown : WorldInfoFlag.None),
|
||||
MoonType = (byte)Main.moonType,
|
||||
TreeX0 = Main.treeX[0],
|
||||
TreeX1 = Main.treeX[1],
|
||||
TreeX2 = Main.treeX[2],
|
||||
TreeStyle0 = (byte)Main.treeStyle[0],
|
||||
TreeStyle1 = (byte)Main.treeStyle[1],
|
||||
TreeStyle2 = (byte)Main.treeStyle[2],
|
||||
TreeStyle3 = (byte)Main.treeStyle[3],
|
||||
CaveBackX0 = Main.caveBackX[0],
|
||||
CaveBackX1 = Main.caveBackX[1],
|
||||
CaveBackX2 = Main.caveBackX[2],
|
||||
CaveBackStyle0 = (byte)Main.caveBackStyle[0],
|
||||
CaveBackStyle1 = (byte)Main.caveBackStyle[1],
|
||||
CaveBackStyle2 = (byte)Main.caveBackStyle[2],
|
||||
CaveBackStyle3 = (byte)Main.caveBackStyle[3],
|
||||
SetBG0 = (byte)WorldGen.treeBG,
|
||||
SetBG1 = (byte)WorldGen.corruptBG,
|
||||
SetBG2 = (byte)WorldGen.jungleBG,
|
||||
SetBG3 = (byte)WorldGen.snowBG,
|
||||
SetBG4 = (byte)WorldGen.hallowBG,
|
||||
SetBG5 = (byte)WorldGen.crimsonBG,
|
||||
SetBG6 = (byte)WorldGen.desertBG,
|
||||
SetBG7 = (byte)WorldGen.oceanBG,
|
||||
IceBackStyle = (byte)Main.iceBackStyle,
|
||||
JungleBackStyle = (byte)Main.jungleBackStyle,
|
||||
HellBackStyle = (byte)Main.hellBackStyle,
|
||||
WindSpeed = Main.windSpeed,
|
||||
NumberOfClouds = (byte)Main.numClouds,
|
||||
BossFlags = (WorldGen.shadowOrbSmashed ? BossFlags.OrbSmashed : BossFlags.None) |
|
||||
(NPC.downedBoss1 ? BossFlags.DownedBoss1 : BossFlags.None) |
|
||||
(NPC.downedBoss2 ? BossFlags.DownedBoss2 : BossFlags.None) |
|
||||
(NPC.downedBoss3 ? BossFlags.DownedBoss3 : BossFlags.None) |
|
||||
(Main.hardMode ? BossFlags.HardMode : BossFlags.None) |
|
||||
(NPC.downedClown ? BossFlags.DownedClown : BossFlags.None),
|
||||
BossFlags2 = (NPC.downedMechBoss1 ? BossFlags2.DownedMechBoss1 : BossFlags2.None) |
|
||||
(NPC.downedMechBoss2 ? BossFlags2.DownedMechBoss2 : BossFlags2.None) |
|
||||
(NPC.downedMechBoss3 ? BossFlags2.DownedMechBoss3 : BossFlags2.None) |
|
||||
(NPC.downedMechBossAny ? BossFlags2.DownedMechBossAny : BossFlags2.None) |
|
||||
(Main.cloudBGActive == 1f ? BossFlags2.CloudBg : BossFlags2.None) |
|
||||
(WorldGen.crimson ? BossFlags2.Crimson : BossFlags2.None),
|
||||
Rain = Main.maxRaining,
|
||||
WorldName = TShock.Config.UseServerName ? TShock.Config.ServerName : Main.worldName
|
||||
};
|
||||
msg.PackFull(ms);
|
||||
|
|
@ -470,41 +510,35 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
|
||||
public bool Teleport(int tilex, int tiley)
|
||||
public bool Teleport(float x, float y, byte style = 1)
|
||||
{
|
||||
InitSpawn = false;
|
||||
|
||||
SendWorldInfo(tilex, tiley, true);
|
||||
|
||||
//150 Should avoid all client crash errors
|
||||
//The error occurs when a tile trys to update which the client hasnt load yet, Clients only update tiles withen 150 blocks
|
||||
//Try 300 if it does not work (Higher number - Longer load times - Less chance of error)
|
||||
//Should we properly send sections so that clients don't get tiles twice?
|
||||
SendTileSquare(tilex, tiley, 150);
|
||||
|
||||
/* //We shouldn't need this section anymore -it can prevent otherwise acceptable teleportation under some circumstances.
|
||||
|
||||
if (!SendTileSquare(tilex, tiley, 150))
|
||||
if (x > Main.rightWorld - 992)
|
||||
{
|
||||
InitSpawn = true;
|
||||
SendWorldInfo(Main.spawnTileX, Main.spawnTileY, false);
|
||||
return false;
|
||||
x = Main.rightWorld - 992;
|
||||
}
|
||||
if (x < 992)
|
||||
{
|
||||
x = 992;
|
||||
}
|
||||
if (y > Main.bottomWorld - 992)
|
||||
{
|
||||
y = Main.bottomWorld - 992;
|
||||
}
|
||||
if (y < 992)
|
||||
{
|
||||
y = 992;
|
||||
}
|
||||
|
||||
*/
|
||||
Spawn(-1, -1);
|
||||
|
||||
SendWorldInfo(Main.spawnTileX, Main.spawnTileY, false);
|
||||
|
||||
TPlayer.position.X = (float)(tilex * 16 + 8 - TPlayer.width /2);
|
||||
TPlayer.position.Y = (float)(tiley * 16 - TPlayer.height);
|
||||
//We need to send the tile data again to prevent clients from thinking they *really* destroyed blocks just now.
|
||||
|
||||
SendTileSquare(tilex, tiley, 10);
|
||||
|
||||
TPlayer.Teleport(new Vector2(x, y), style);
|
||||
NetMessage.SendData((int)PacketTypes.Teleport, -1, -1, "", 0, TPlayer.whoAmi, x, y, style);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Heal(int damage = 400)
|
||||
{
|
||||
NetMessage.SendData((int)PacketTypes.PlayerHealOther, -1, -1, "", this.TPlayer.whoAmi, damage);
|
||||
}
|
||||
|
||||
public void Spawn()
|
||||
{
|
||||
Spawn(TPlayer.SpawnX, TPlayer.SpawnY);
|
||||
|
|
@ -594,7 +628,7 @@ namespace TShockAPI
|
|||
|
||||
public virtual void GiveItem(int type, string name, int width, int height, int stack, int prefix = 0)
|
||||
{
|
||||
int itemid = Item.NewItem((int) X, (int) Y, width, height, type, stack, true, prefix);
|
||||
int itemid = Item.NewItem((int) X, (int) Y, width, height, type, stack, true, prefix, true);
|
||||
|
||||
// This is for special pickaxe/hammers/swords etc
|
||||
Main.item[itemid].SetDefaults(name);
|
||||
|
|
@ -604,8 +638,10 @@ namespace TShockAPI
|
|||
Main.item[itemid].stack = stack;
|
||||
Main.item[itemid].owner = Index;
|
||||
Main.item[itemid].prefix = (byte) prefix;
|
||||
NetMessage.SendData((int) PacketTypes.ItemDrop, -1, -1, "", itemid, 0f, 0f, 0f);
|
||||
NetMessage.SendData((int) PacketTypes.ItemOwner, -1, -1, "", itemid, 0f, 0f, 0f);
|
||||
Main.item[itemid].noGrabDelay = 1;
|
||||
Main.item[itemid].velocity = Main.player[this.Index].velocity;
|
||||
NetMessage.SendData((int)PacketTypes.ItemDrop, -1, -1, "", itemid, 0f, 0f, 0f);
|
||||
NetMessage.SendData((int)PacketTypes.ItemOwner, -1, -1, "", itemid, 0f, 0f, 0f);
|
||||
}
|
||||
|
||||
public virtual void SendInfoMessage(string msg)
|
||||
|
|
@ -876,6 +912,12 @@ namespace TShockAPI
|
|||
Main.bloodMoon = bloodMoon;
|
||||
SetTime(false, 0);
|
||||
}
|
||||
|
||||
public void SetEclipse(bool Eclipse)
|
||||
{
|
||||
Main.eclipse = Eclipse;
|
||||
SetTime(true, 150);
|
||||
}
|
||||
|
||||
public void SetTime(bool dayTime, double time)
|
||||
{
|
||||
|
|
@ -910,12 +952,12 @@ namespace TShockAPI
|
|||
NetMessage.SendData((int) PacketTypes.NpcStrike, -1, -1, "", npcid, damage, knockBack, hitDirection);
|
||||
}
|
||||
|
||||
public void RevertTiles(Dictionary<Vector2, TileData> tiles)
|
||||
public void RevertTiles(Dictionary<Vector2, Tile> tiles)
|
||||
{
|
||||
// Update Main.Tile first so that when tile sqaure is sent it is correct
|
||||
foreach (KeyValuePair<Vector2, TileData> entry in tiles)
|
||||
foreach (KeyValuePair<Vector2, Tile> entry in tiles)
|
||||
{
|
||||
Main.tile[(int) entry.Key.X, (int) entry.Key.Y].Data = entry.Value;
|
||||
Main.tile[(int) entry.Key.X, (int) entry.Key.Y] = entry.Value;
|
||||
}
|
||||
// Send all players updated tile sqaures
|
||||
foreach (Vector2 coords in tiles.Keys)
|
||||
|
|
@ -977,9 +1019,10 @@ namespace TShockAPI
|
|||
this.maxHealth = player.TPlayer.statLifeMax;
|
||||
Item[] inventory = player.TPlayer.inventory;
|
||||
Item[] armor = player.TPlayer.armor;
|
||||
Item[] dye = player.TPlayer.dye;
|
||||
for (int i = 0; i < NetItem.maxNetInventory; i++)
|
||||
{
|
||||
if (i < 49)
|
||||
if (i < NetItem.maxNetInventory - (NetItem.armorSlots + NetItem.dyeSlots))
|
||||
{
|
||||
if (player.TPlayer.inventory[i] != null)
|
||||
{
|
||||
|
|
@ -1001,11 +1044,12 @@ namespace TShockAPI
|
|||
this.inventory[i].prefix = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (i < NetItem.maxNetInventory - NetItem.dyeSlots)
|
||||
{
|
||||
if (player.TPlayer.armor[i - 48] != null)
|
||||
var index = i - (NetItem.maxNetInventory - (NetItem.armorSlots + NetItem.dyeSlots));
|
||||
if (player.TPlayer.armor[index] != null)
|
||||
{
|
||||
this.inventory[i].netID = armor[i - 48].netID;
|
||||
this.inventory[i].netID = armor[index].netID;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1014,8 +1058,31 @@ namespace TShockAPI
|
|||
|
||||
if (this.inventory[i].netID != 0)
|
||||
{
|
||||
this.inventory[i].stack = armor[i - 48].stack;
|
||||
this.inventory[i].prefix = armor[i - 48].prefix;
|
||||
this.inventory[i].stack = armor[index].stack;
|
||||
this.inventory[i].prefix = armor[index].prefix;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.inventory[i].stack = 0;
|
||||
this.inventory[i].prefix = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var index = i - (NetItem.maxNetInventory - NetItem.dyeSlots);
|
||||
if (player.TPlayer.dye[index] != null)
|
||||
{
|
||||
this.inventory[i].netID = armor[index].netID;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.inventory[i].netID = 0;
|
||||
}
|
||||
|
||||
if (this.inventory[i].netID != 0)
|
||||
{
|
||||
this.inventory[i].stack = armor[index].stack;
|
||||
this.inventory[i].prefix = armor[index].prefix;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1029,11 +1096,13 @@ namespace TShockAPI
|
|||
|
||||
public class NetItem
|
||||
{
|
||||
public static int maxNetInventory = 59;
|
||||
public static readonly int maxNetInventory = 73;
|
||||
public static readonly int armorSlots = 11;
|
||||
public static readonly int dyeSlots = 3;
|
||||
public int netID;
|
||||
public int stack;
|
||||
public int prefix;
|
||||
|
||||
|
||||
public static string ToString(NetItem[] inventory)
|
||||
{
|
||||
string inventoryString = "";
|
||||
|
|
|
|||
|
|
@ -27,19 +27,19 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using Hooks;
|
||||
using MaxMind;
|
||||
using Mono.Data.Sqlite;
|
||||
using MySql.Data.MySqlClient;
|
||||
using Newtonsoft.Json;
|
||||
using Rests;
|
||||
using Terraria;
|
||||
using TerrariaApi.Server;
|
||||
using TShockAPI.DB;
|
||||
using TShockAPI.Net;
|
||||
|
||||
namespace TShockAPI
|
||||
{
|
||||
[APIVersion(1, 13)]
|
||||
[ApiVersion(1, 14)]
|
||||
public class TShock : TerrariaPlugin
|
||||
{
|
||||
public static readonly Version VersionNum = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
|
|
@ -231,25 +231,25 @@ namespace TShockAPI
|
|||
|
||||
Log.ConsoleInfo(string.Format("|> Version {0} ({1}) now running.", Version, VersionCodename));
|
||||
|
||||
GameHooks.PostInitialize += OnPostInit;
|
||||
GameHooks.Update += OnUpdate;
|
||||
GameHooks.HardUpdate += OnHardUpdate;
|
||||
GameHooks.StatueSpawn += OnStatueSpawn;
|
||||
ServerHooks.Connect += OnConnect;
|
||||
ServerHooks.Join += OnJoin;
|
||||
ServerHooks.Leave += OnLeave;
|
||||
ServerHooks.Chat += OnChat;
|
||||
ServerHooks.Command += ServerHooks_OnCommand;
|
||||
NetHooks.GetData += OnGetData;
|
||||
NetHooks.SendData += NetHooks_SendData;
|
||||
NetHooks.GreetPlayer += OnGreetPlayer;
|
||||
NpcHooks.StrikeNpc += NpcHooks_OnStrikeNpc;
|
||||
NpcHooks.SetDefaultsInt += OnNpcSetDefaults;
|
||||
ProjectileHooks.SetDefaults += OnProjectileSetDefaults;
|
||||
WorldHooks.StartHardMode += OnStartHardMode;
|
||||
WorldHooks.SaveWorld += SaveManager.Instance.OnSaveWorld;
|
||||
WorldHooks.ChristmasCheck += OnXmasCheck;
|
||||
NetHooks.NameCollision += NetHooks_NameCollision;
|
||||
ServerApi.Hooks.GamePostInitialize.Register(this, OnPostInit);
|
||||
ServerApi.Hooks.GameUpdate.Register(this, OnUpdate);
|
||||
ServerApi.Hooks.GameHardmodeTileUpdate.Register(this, OnHardUpdate);
|
||||
ServerApi.Hooks.GameStatueSpawn.Register(this, OnStatueSpawn);
|
||||
ServerApi.Hooks.ServerConnect.Register(this, OnConnect);
|
||||
ServerApi.Hooks.ServerJoin.Register(this, OnJoin);
|
||||
ServerApi.Hooks.ServerLeave.Register(this, OnLeave);
|
||||
ServerApi.Hooks.ServerChat.Register(this, OnChat);
|
||||
ServerApi.Hooks.ServerCommand.Register(this, ServerHooks_OnCommand);
|
||||
ServerApi.Hooks.NetGetData.Register(this, OnGetData);
|
||||
ServerApi.Hooks.NetSendData.Register(this, NetHooks_SendData);
|
||||
ServerApi.Hooks.NetGreetPlayer.Register(this, OnGreetPlayer);
|
||||
ServerApi.Hooks.NpcStrike.Register(this, NpcHooks_OnStrikeNpc);
|
||||
ServerApi.Hooks.NpcSetDefaultsInt.Register(this, OnNpcSetDefaults);
|
||||
ServerApi.Hooks.ProjectileSetDefaults.Register(this, OnProjectileSetDefaults);
|
||||
ServerApi.Hooks.WorldStartHardMode.Register(this, OnStartHardMode);
|
||||
ServerApi.Hooks.WorldSave.Register(this, SaveManager.Instance.OnSaveWorld);
|
||||
ServerApi.Hooks.WorldChristmasCheck.Register(this, OnXmasCheck);
|
||||
ServerApi.Hooks.NetNameCollision.Register(this, NetHooks_NameCollision);
|
||||
TShockAPI.Hooks.PlayerHooks.PlayerPostLogin += OnPlayerLogin;
|
||||
|
||||
GetDataHandlers.InitGetDataHandler();
|
||||
|
|
@ -260,7 +260,7 @@ namespace TShockAPI
|
|||
RestApi.Start();
|
||||
|
||||
if (Config.BufferPackets)
|
||||
PacketBuffer = new PacketBufferer();
|
||||
PacketBuffer = new PacketBufferer(this);
|
||||
|
||||
Log.ConsoleInfo("AutoSave " + (Config.AutoSave ? "Enabled" : "Disabled"));
|
||||
Log.ConsoleInfo("Backups " + (Backups.Interval > 0 ? "Enabled" : "Disabled"));
|
||||
|
|
@ -306,25 +306,26 @@ namespace TShockAPI
|
|||
}
|
||||
SaveManager.Instance.Dispose();
|
||||
|
||||
GameHooks.PostInitialize -= OnPostInit;
|
||||
GameHooks.Update -= OnUpdate;
|
||||
GameHooks.HardUpdate -= OnHardUpdate;
|
||||
GameHooks.StatueSpawn -= OnStatueSpawn;
|
||||
ServerHooks.Connect -= OnConnect;
|
||||
ServerHooks.Join -= OnJoin;
|
||||
ServerHooks.Leave -= OnLeave;
|
||||
ServerHooks.Chat -= OnChat;
|
||||
ServerHooks.Command -= ServerHooks_OnCommand;
|
||||
NetHooks.GetData -= OnGetData;
|
||||
NetHooks.SendData -= NetHooks_SendData;
|
||||
NetHooks.GreetPlayer -= OnGreetPlayer;
|
||||
NpcHooks.StrikeNpc -= NpcHooks_OnStrikeNpc;
|
||||
NpcHooks.SetDefaultsInt -= OnNpcSetDefaults;
|
||||
ProjectileHooks.SetDefaults -= OnProjectileSetDefaults;
|
||||
WorldHooks.StartHardMode -= OnStartHardMode;
|
||||
WorldHooks.SaveWorld -= SaveManager.Instance.OnSaveWorld;
|
||||
WorldHooks.ChristmasCheck -= OnXmasCheck;
|
||||
NetHooks.NameCollision -= NetHooks_NameCollision;
|
||||
|
||||
ServerApi.Hooks.GamePostInitialize.Deregister(this, OnPostInit);
|
||||
ServerApi.Hooks.GameUpdate.Deregister(this, OnUpdate);
|
||||
ServerApi.Hooks.GameHardmodeTileUpdate.Deregister(this, OnHardUpdate);
|
||||
ServerApi.Hooks.GameStatueSpawn.Deregister(this, OnStatueSpawn);
|
||||
ServerApi.Hooks.ServerConnect.Deregister(this, OnConnect);
|
||||
ServerApi.Hooks.ServerJoin.Deregister(this, OnJoin);
|
||||
ServerApi.Hooks.ServerLeave.Deregister(this, OnLeave);
|
||||
ServerApi.Hooks.ServerChat.Deregister(this, OnChat);
|
||||
ServerApi.Hooks.ServerCommand.Deregister(this, ServerHooks_OnCommand);
|
||||
ServerApi.Hooks.NetGetData.Deregister(this, OnGetData);
|
||||
ServerApi.Hooks.NetSendData.Deregister(this, NetHooks_SendData);
|
||||
ServerApi.Hooks.NetGreetPlayer.Deregister(this, OnGreetPlayer);
|
||||
ServerApi.Hooks.NpcStrike.Deregister(this, NpcHooks_OnStrikeNpc);
|
||||
ServerApi.Hooks.NpcSetDefaultsInt.Deregister(this, OnNpcSetDefaults);
|
||||
ServerApi.Hooks.ProjectileSetDefaults.Deregister(this, OnProjectileSetDefaults);
|
||||
ServerApi.Hooks.WorldStartHardMode.Deregister(this, OnStartHardMode);
|
||||
ServerApi.Hooks.WorldSave.Deregister(this, SaveManager.Instance.OnSaveWorld);
|
||||
ServerApi.Hooks.WorldChristmasCheck.Deregister(this, OnXmasCheck);
|
||||
ServerApi.Hooks.NetNameCollision.Deregister(this, NetHooks_NameCollision);
|
||||
TShockAPI.Hooks.PlayerHooks.PlayerPostLogin -= OnPlayerLogin;
|
||||
|
||||
if (File.Exists(Path.Combine(SavePath, "tshock.pid")))
|
||||
|
|
@ -362,34 +363,34 @@ namespace TShockAPI
|
|||
Users.UpdateLogin(u);
|
||||
}
|
||||
|
||||
private void NetHooks_NameCollision(int who, string name, HandledEventArgs e)
|
||||
private void NetHooks_NameCollision(NameCollisionEventArgs args)
|
||||
{
|
||||
string ip = TShock.Utils.GetRealIP(Netplay.serverSock[who].tcpClient.Client.RemoteEndPoint.ToString());
|
||||
string ip = TShock.Utils.GetRealIP(Netplay.serverSock[args.Who].tcpClient.Client.RemoteEndPoint.ToString());
|
||||
foreach (TSPlayer ply in TShock.Players)
|
||||
{
|
||||
if (ply == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (ply.Name == name && ply.Index != who)
|
||||
if (ply.Name == args.Name && ply.Index != args.Who)
|
||||
{
|
||||
if (ply.IP == ip)
|
||||
{
|
||||
if (ply.State < 2)
|
||||
{
|
||||
Utils.ForceKick(ply, "Name collision and this client has no world data.", true, false);
|
||||
e.Handled = true;
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Handled = false;
|
||||
args.Handled = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
e.Handled = false;
|
||||
args.Handled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -533,7 +534,7 @@ namespace TShockAPI
|
|||
|
||||
public static int AuthToken = -1;
|
||||
|
||||
private void OnPostInit()
|
||||
private void OnPostInit(EventArgs args)
|
||||
{
|
||||
SetConsoleTitle();
|
||||
if (!File.Exists(Path.Combine(SavePath, "auth.lck")) && !File.Exists(Path.Combine(SavePath, "authcode.txt")))
|
||||
|
|
@ -597,7 +598,7 @@ namespace TShockAPI
|
|||
private DateTime LastCheck = DateTime.UtcNow;
|
||||
private DateTime LastSave = DateTime.UtcNow;
|
||||
|
||||
private void OnUpdate()
|
||||
private void OnUpdate(EventArgs args)
|
||||
{
|
||||
UpdateManager.UpdateProcedureCheck();
|
||||
if (Backups.IsBackupTime)
|
||||
|
|
@ -608,7 +609,7 @@ namespace TShockAPI
|
|||
OnSecondUpdate();
|
||||
LastCheck = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
|
||||
if ((DateTime.UtcNow - LastSave).TotalMinutes >= Config.ServerSideInventorySave)
|
||||
{
|
||||
foreach (TSPlayer player in Players)
|
||||
|
|
@ -638,12 +639,11 @@ namespace TShockAPI
|
|||
break;
|
||||
}
|
||||
}
|
||||
int count = 0;
|
||||
|
||||
foreach (TSPlayer player in Players)
|
||||
{
|
||||
if (player != null && player.Active)
|
||||
{
|
||||
count++;
|
||||
if (player.TilesDestroyed != null)
|
||||
{
|
||||
if (player.TileKillThreshold >= Config.TileKillThreshold)
|
||||
|
|
@ -672,6 +672,10 @@ namespace TShockAPI
|
|||
{
|
||||
player.TilePlaceThreshold = 0;
|
||||
}
|
||||
|
||||
if (player.RecentFuse >0)
|
||||
player.RecentFuse--;
|
||||
|
||||
if (player.TileLiquidThreshold >= Config.TileLiquidThreshold)
|
||||
{
|
||||
player.Disable("Reached TileLiquid threshold.");
|
||||
|
|
@ -737,7 +741,7 @@ namespace TShockAPI
|
|||
Config.MaxSlots, Netplay.serverListenIP, Netplay.serverPort, Version);
|
||||
}
|
||||
|
||||
private void OnHardUpdate( HardUpdateEventArgs args )
|
||||
private void OnHardUpdate(HardmodeTileUpdateEventArgs args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
|
@ -767,14 +771,14 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
|
||||
private void OnConnect(int ply, HandledEventArgs handler)
|
||||
private void OnConnect(ConnectEventArgs args)
|
||||
{
|
||||
var player = new TSPlayer(ply);
|
||||
var player = new TSPlayer(args.Who);
|
||||
|
||||
if (Utils.ActivePlayers() + 1 > Config.MaxSlots + Config.ReservedSlots)
|
||||
{
|
||||
Utils.ForceKick(player, Config.ServerFullNoReservedReason, true, false);
|
||||
handler.Handled = true;
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -790,7 +794,7 @@ namespace TShockAPI
|
|||
DateTime exp;
|
||||
string duration = DateTime.TryParse(ban.Expiration, out exp) ? String.Format("until {0}", exp.ToString("G")) : "forever";
|
||||
Utils.ForceKick(player, string.Format("You are banned {0}: {1}", duration, ban.Reason), true, false);
|
||||
handler.Handled = true;
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -798,7 +802,7 @@ namespace TShockAPI
|
|||
if (!FileTools.OnWhitelist(player.IP))
|
||||
{
|
||||
Utils.ForceKick(player, Config.WhitelistKickReason, true, false);
|
||||
handler.Handled = true;
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -811,20 +815,20 @@ namespace TShockAPI
|
|||
if (Config.KickProxyUsers)
|
||||
{
|
||||
Utils.ForceKick(player, "Proxies are not allowed.", true, false);
|
||||
handler.Handled = true;
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Players[ply] = player;
|
||||
Players[args.Who] = player;
|
||||
}
|
||||
|
||||
private void OnJoin(int ply, HandledEventArgs handler)
|
||||
private void OnJoin(JoinEventArgs args)
|
||||
{
|
||||
var player = Players[ply];
|
||||
var player = Players[args.Who];
|
||||
if (player == null)
|
||||
{
|
||||
handler.Handled = true;
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -848,16 +852,16 @@ namespace TShockAPI
|
|||
DateTime exp;
|
||||
string duration = DateTime.TryParse(ban.Expiration, out exp) ? String.Format("until {0}", exp.ToString("G")) : "forever";
|
||||
Utils.ForceKick(player, string.Format("You are banned {0}: {1}", duration, ban.Reason), true, false);
|
||||
handler.Handled = true;
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLeave(int ply)
|
||||
private void OnLeave(LeaveEventArgs args)
|
||||
{
|
||||
|
||||
var tsplr = Players[ply];
|
||||
Players[ply] = null;
|
||||
var tsplr = Players[args.Who];
|
||||
Players[args.Who] = null;
|
||||
|
||||
if (tsplr != null && tsplr.ReceivedInfo)
|
||||
{
|
||||
|
|
@ -880,15 +884,15 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
|
||||
private void OnChat(messageBuffer msg, int ply, string text, HandledEventArgs e)
|
||||
private void OnChat(ServerChatEventArgs args)
|
||||
{
|
||||
if (e.Handled)
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
var tsplr = Players[msg.whoAmI];
|
||||
var tsplr = Players[args.Who];
|
||||
if (tsplr == null)
|
||||
{
|
||||
e.Handled = true;
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -898,11 +902,11 @@ namespace TShockAPI
|
|||
return;
|
||||
}*/
|
||||
|
||||
if (text.StartsWith("/"))
|
||||
if (args.Text.StartsWith("/"))
|
||||
{
|
||||
try
|
||||
{
|
||||
e.Handled = Commands.HandleCommand(tsplr, text);
|
||||
args.Handled = Commands.HandleCommand(tsplr, args.Text);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -913,18 +917,18 @@ namespace TShockAPI
|
|||
else if (!tsplr.mute && !TShock.Config.EnableChatAboveHeads)
|
||||
{
|
||||
Utils.Broadcast(
|
||||
String.Format(Config.ChatFormat, tsplr.Group.Name, tsplr.Group.Prefix, tsplr.Name, tsplr.Group.Suffix, text),
|
||||
String.Format(Config.ChatFormat, tsplr.Group.Name, tsplr.Group.Prefix, tsplr.Name, tsplr.Group.Suffix, args.Text),
|
||||
tsplr.Group.R, tsplr.Group.G, tsplr.Group.B);
|
||||
e.Handled = true;
|
||||
args.Handled = true;
|
||||
} else if (!tsplr.mute && TShock.Config.EnableChatAboveHeads)
|
||||
{
|
||||
Utils.Broadcast(ply, String.Format(Config.ChatAboveHeadsFormat, tsplr.Group.Name, tsplr.Group.Prefix, tsplr.Name, tsplr.Group.Suffix, text), tsplr.Group.R, tsplr.Group.G, tsplr.Group.B);
|
||||
e.Handled = true;
|
||||
Utils.Broadcast(args.Who, String.Format(Config.ChatAboveHeadsFormat, tsplr.Group.Name, tsplr.Group.Prefix, tsplr.Name, tsplr.Group.Suffix, args.Text), tsplr.Group.R, tsplr.Group.G, tsplr.Group.B);
|
||||
args.Handled = true;
|
||||
}
|
||||
else if (tsplr.mute)
|
||||
{
|
||||
tsplr.SendErrorMessage("You are muted!");
|
||||
e.Handled = true;
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -933,9 +937,9 @@ namespace TShockAPI
|
|||
/// </summary>
|
||||
/// <param name="cmd"></param>
|
||||
/// <param name="e"></param>
|
||||
private void ServerHooks_OnCommand(string text, HandledEventArgs e)
|
||||
private void ServerHooks_OnCommand(CommandEventArgs args)
|
||||
{
|
||||
if (e.Handled)
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
// Damn you ThreadStatic and Redigit
|
||||
|
|
@ -948,7 +952,7 @@ namespace TShockAPI
|
|||
WorldGen.genRand = new Random();
|
||||
}
|
||||
|
||||
if (text.StartsWith("playing") || text.StartsWith("/playing"))
|
||||
if (args.Command.StartsWith("playing") || args.Command.StartsWith("/playing"))
|
||||
{
|
||||
int count = 0;
|
||||
foreach (TSPlayer player in Players)
|
||||
|
|
@ -962,20 +966,20 @@ namespace TShockAPI
|
|||
}
|
||||
TSPlayer.Server.SendInfoMessage(string.Format("{0} players connected.", count));
|
||||
}
|
||||
else if (text == "autosave")
|
||||
else if (args.Command == "autosave")
|
||||
{
|
||||
Main.autoSave = Config.AutoSave = !Config.AutoSave;
|
||||
Log.ConsoleInfo("AutoSave " + (Config.AutoSave ? "Enabled" : "Disabled"));
|
||||
}
|
||||
else if (text.StartsWith("/"))
|
||||
else if (args.Command.StartsWith("/"))
|
||||
{
|
||||
Commands.HandleCommand(TSPlayer.Server, text);
|
||||
Commands.HandleCommand(TSPlayer.Server, args.Command);
|
||||
}
|
||||
else
|
||||
{
|
||||
Commands.HandleCommand(TSPlayer.Server, "/" + text);
|
||||
Commands.HandleCommand(TSPlayer.Server, "/" + args.Command);
|
||||
}
|
||||
e.Handled = true;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnGetData(GetDataEventArgs e)
|
||||
|
|
@ -1027,12 +1031,12 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
|
||||
private void OnGreetPlayer(int who, HandledEventArgs e)
|
||||
private void OnGreetPlayer(GreetPlayerEventArgs args)
|
||||
{
|
||||
var player = Players[who];
|
||||
var player = Players[args.Who];
|
||||
if (player == null)
|
||||
{
|
||||
e.Handled = true;
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
player.LoginMS= DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
|
||||
|
|
@ -1072,10 +1076,13 @@ namespace TShockAPI
|
|||
if (RememberedPos.GetLeavePos(player.Name, player.IP) != Vector2.Zero){
|
||||
var pos = RememberedPos.GetLeavePos(player.Name, player.IP);
|
||||
|
||||
player.Teleport((int) pos.X, (int) pos.Y + 3);
|
||||
player.Teleport(pos.X*16, pos.Y*16 + 48);
|
||||
}}
|
||||
|
||||
e.Handled = true;
|
||||
Item i = new Item();
|
||||
i.SetDefaults("Confetti Gun");
|
||||
player.GiveItem(i.type, i.name, i.width, i.headSlot, 10);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void NpcHooks_OnStrikeNpc(NpcStrikeEventArgs e)
|
||||
|
|
@ -1152,7 +1159,7 @@ namespace TShockAPI
|
|||
|
||||
private void NetHooks_SendData(SendDataEventArgs e)
|
||||
{
|
||||
if (e.MsgID == PacketTypes.Disconnect)
|
||||
if (e.MsgId == PacketTypes.Disconnect)
|
||||
{
|
||||
Action<ServerSock, string> senddisconnect = (sock, str) =>
|
||||
{
|
||||
|
|
@ -1182,7 +1189,7 @@ namespace TShockAPI
|
|||
}
|
||||
e.Handled = true;
|
||||
}
|
||||
if (e.MsgID == PacketTypes.WorldInfo)
|
||||
if (e.MsgId == PacketTypes.WorldInfo)
|
||||
{
|
||||
if (e.remoteClient == -1) return;
|
||||
var player = Players[e.remoteClient];
|
||||
|
|
@ -1193,26 +1200,62 @@ namespace TShockAPI
|
|||
{
|
||||
var msg = new WorldInfoMsg
|
||||
{
|
||||
Time = (int)Main.time,
|
||||
DayTime = Main.dayTime,
|
||||
MoonPhase = (byte)Main.moonPhase,
|
||||
BloodMoon = Main.bloodMoon,
|
||||
MaxTilesX = Main.maxTilesX,
|
||||
MaxTilesY = Main.maxTilesY,
|
||||
SpawnX = Main.spawnTileX,
|
||||
SpawnY = Main.spawnTileY,
|
||||
WorldSurface = (int)Main.worldSurface,
|
||||
RockLayer = (int)Main.rockLayer,
|
||||
WorldID = Main.worldID,
|
||||
WorldFlags =
|
||||
(WorldGen.shadowOrbSmashed ? WorldInfoFlag.OrbSmashed : WorldInfoFlag.None) |
|
||||
(NPC.downedBoss1 ? WorldInfoFlag.DownedBoss1 : WorldInfoFlag.None) |
|
||||
(NPC.downedBoss2 ? WorldInfoFlag.DownedBoss2 : WorldInfoFlag.None) |
|
||||
(NPC.downedBoss3 ? WorldInfoFlag.DownedBoss3 : WorldInfoFlag.None) |
|
||||
(Main.hardMode ? WorldInfoFlag.HardMode : WorldInfoFlag.None) |
|
||||
(NPC.downedClown ? WorldInfoFlag.DownedClown : WorldInfoFlag.None),
|
||||
WorldName = Config.ServerName
|
||||
};
|
||||
Time = (int) Main.time,
|
||||
DayTime = Main.dayTime,
|
||||
MoonPhase = (byte) Main.moonPhase,
|
||||
BloodMoon = Main.bloodMoon,
|
||||
MaxTilesX = Main.maxTilesX,
|
||||
MaxTilesY = Main.maxTilesY,
|
||||
SpawnX = Main.spawnTileX,
|
||||
SpawnY = Main.spawnTileY,
|
||||
WorldSurface = (int) Main.worldSurface,
|
||||
RockLayer = (int) Main.rockLayer,
|
||||
//Sending a fake world id causes the client to not be able to find a stored spawnx/y.
|
||||
//This fixes the bed spawn point bug. With a fake world id it wont be able to find the bed spawn.
|
||||
WorldID = Main.worldID,
|
||||
MoonType = (byte)Main.moonType,
|
||||
TreeX0 = Main.treeX[0],
|
||||
TreeX1 = Main.treeX[1],
|
||||
TreeX2 = Main.treeX[2],
|
||||
TreeStyle0 = (byte)Main.treeStyle[0],
|
||||
TreeStyle1 = (byte)Main.treeStyle[1],
|
||||
TreeStyle2 = (byte)Main.treeStyle[2],
|
||||
TreeStyle3 = (byte)Main.treeStyle[3],
|
||||
CaveBackX0 = Main.caveBackX[0],
|
||||
CaveBackX1 = Main.caveBackX[1],
|
||||
CaveBackX2 = Main.caveBackX[2],
|
||||
CaveBackStyle0 = (byte)Main.caveBackStyle[0],
|
||||
CaveBackStyle1 = (byte)Main.caveBackStyle[1],
|
||||
CaveBackStyle2 = (byte)Main.caveBackStyle[2],
|
||||
CaveBackStyle3 = (byte)Main.caveBackStyle[3],
|
||||
SetBG0 = (byte)WorldGen.treeBG,
|
||||
SetBG1 = (byte)WorldGen.corruptBG,
|
||||
SetBG2 = (byte)WorldGen.jungleBG,
|
||||
SetBG3 = (byte)WorldGen.snowBG,
|
||||
SetBG4 = (byte)WorldGen.hallowBG,
|
||||
SetBG5 = (byte)WorldGen.crimsonBG,
|
||||
SetBG6 = (byte)WorldGen.desertBG,
|
||||
SetBG7 = (byte)WorldGen.oceanBG,
|
||||
IceBackStyle = (byte)Main.iceBackStyle,
|
||||
JungleBackStyle = (byte)Main.jungleBackStyle,
|
||||
HellBackStyle = (byte)Main.hellBackStyle,
|
||||
WindSpeed = Main.windSpeed,
|
||||
NumberOfClouds = (byte)Main.numClouds,
|
||||
BossFlags = (WorldGen.shadowOrbSmashed ? BossFlags.OrbSmashed : BossFlags.None) |
|
||||
(NPC.downedBoss1 ? BossFlags.DownedBoss1 : BossFlags.None) |
|
||||
(NPC.downedBoss2 ? BossFlags.DownedBoss2 : BossFlags.None) |
|
||||
(NPC.downedBoss3 ? BossFlags.DownedBoss3 : BossFlags.None) |
|
||||
(Main.hardMode ? BossFlags.HardMode : BossFlags.None) |
|
||||
(NPC.downedClown ? BossFlags.DownedClown : BossFlags.None),
|
||||
BossFlags2 = (NPC.downedMechBoss1 ? BossFlags2.DownedMechBoss1 : BossFlags2.None) |
|
||||
(NPC.downedMechBoss2 ? BossFlags2.DownedMechBoss2 : BossFlags2.None) |
|
||||
(NPC.downedMechBoss3 ? BossFlags2.DownedMechBoss3 : BossFlags2.None) |
|
||||
(NPC.downedMechBossAny ? BossFlags2.DownedMechBossAny : BossFlags2.None) |
|
||||
(Main.cloudBGActive == 1f ? BossFlags2.CloudBg : BossFlags2.None) |
|
||||
(WorldGen.crimson ? BossFlags2.Crimson : BossFlags2.None),
|
||||
Rain = Main.maxRaining,
|
||||
WorldName = TShock.Config.UseServerName ? TShock.Config.ServerName : Main.worldName
|
||||
};
|
||||
msg.PackFull(ms);
|
||||
player.SendRawData(ms.ToArray());
|
||||
}
|
||||
|
|
@ -1332,11 +1375,11 @@ namespace TShockAPI
|
|||
return false;
|
||||
}
|
||||
|
||||
public static bool CheckTilePermission( TSPlayer player, int tileX, int tileY, byte tileType, byte actionType )
|
||||
public static bool CheckTilePermission( TSPlayer player, int tileX, int tileY, byte tileType, GetDataHandlers.EditAction actionType )
|
||||
{
|
||||
if (!player.Group.HasPermission(Permissions.canbuild))
|
||||
{
|
||||
if (TShock.Config.AllowIce && actionType != 1)
|
||||
if (TShock.Config.AllowIce && actionType != GetDataHandlers.EditAction.PlaceTile)
|
||||
{
|
||||
|
||||
foreach (Point p in player.IceTiles)
|
||||
|
|
@ -1356,7 +1399,7 @@ namespace TShockAPI
|
|||
return true;
|
||||
}
|
||||
|
||||
if (TShock.Config.AllowIce && actionType == 1 && tileType == 127)
|
||||
if (TShock.Config.AllowIce && actionType == GetDataHandlers.EditAction.PlaceTile && tileType == 127)
|
||||
{
|
||||
player.IceTiles.Add(new Point(tileX, tileY));
|
||||
return false;
|
||||
|
|
@ -1387,12 +1430,12 @@ namespace TShockAPI
|
|||
{
|
||||
if (!player.Group.HasPermission(Permissions.editspawn))
|
||||
{
|
||||
if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.WPm) > 2000){
|
||||
player.SendMessage("The world is protected from changes.", Color.Red);
|
||||
player.WPm=DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
|
||||
|
||||
}
|
||||
return true;
|
||||
if (((DateTime.Now.Ticks/TimeSpan.TicksPerMillisecond) - player.WPm) > 2000)
|
||||
{
|
||||
player.SendMessage("The world is protected from changes.", Color.Red);
|
||||
player.WPm = DateTime.Now.Ticks/TimeSpan.TicksPerMillisecond;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (Config.SpawnProtection)
|
||||
|
|
@ -1498,9 +1541,10 @@ namespace TShockAPI
|
|||
|
||||
Item[] inventory = player.TPlayer.inventory;
|
||||
Item[] armor = player.TPlayer.armor;
|
||||
Item[] dye = player.TPlayer.dye;
|
||||
for (int i = 0; i < NetItem.maxNetInventory; i++)
|
||||
{
|
||||
if (i < 49)
|
||||
if (i < NetItem.maxNetInventory - (NetItem.armorSlots + NetItem.dyeSlots))
|
||||
{
|
||||
Item item = new Item();
|
||||
if (inventory[i] != null && inventory[i].netID != 0)
|
||||
|
|
@ -1517,15 +1561,16 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else if(i < (NetItem.maxNetInventory - (NetItem.armorSlots + NetItem.dyeSlots)))
|
||||
{
|
||||
Item item = new Item();
|
||||
if (armor[i - 48] != null && armor[i - 48].netID != 0)
|
||||
var index = i - (NetItem.maxNetInventory - (NetItem.armorSlots + NetItem.dyeSlots));
|
||||
if (armor[index] != null && armor[index].netID != 0)
|
||||
{
|
||||
item.netDefaults(armor[i - 48].netID);
|
||||
item.Prefix(armor[i - 48].prefix);
|
||||
item.netDefaults(armor[index].netID);
|
||||
item.Prefix(armor[index].prefix);
|
||||
item.AffixName();
|
||||
if (armor[i - 48].stack > item.maxStack)
|
||||
if (armor[index].stack > item.maxStack)
|
||||
{
|
||||
check = true;
|
||||
player.SendMessage(
|
||||
|
|
@ -1534,6 +1579,24 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (i < (NetItem.maxNetInventory - (NetItem.armorSlots + NetItem.dyeSlots)))
|
||||
{
|
||||
Item item = new Item();
|
||||
var index = i - (NetItem.maxNetInventory - NetItem.dyeSlots);
|
||||
if (dye[index] != null && dye[index].netID != 0)
|
||||
{
|
||||
item.netDefaults(dye[index].netID);
|
||||
item.Prefix(dye[index].prefix);
|
||||
item.AffixName();
|
||||
if (dye[index].stack > item.maxStack)
|
||||
{
|
||||
check = true;
|
||||
player.SendMessage(
|
||||
String.Format("Stack cheat detected. Remove dye {0} ({1}) and then rejoin", item.name, dye[index].stack),
|
||||
Color.Cyan);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return check;
|
||||
|
|
@ -1553,9 +1616,10 @@ namespace TShockAPI
|
|||
|
||||
Item[] inventory = player.TPlayer.inventory;
|
||||
Item[] armor = player.TPlayer.armor;
|
||||
Item[] dye = player.TPlayer.dye;
|
||||
for (int i = 0; i < NetItem.maxNetInventory; i++)
|
||||
{
|
||||
if (i < 49)
|
||||
if (i < NetItem.maxNetInventory - (NetItem.armorSlots + NetItem.dyeSlots))
|
||||
{
|
||||
Item item = new Item();
|
||||
Item serverItem = new Item();
|
||||
|
|
@ -1592,34 +1656,35 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else if(i < (NetItem.maxNetInventory - (NetItem.armorSlots + NetItem.dyeSlots)))
|
||||
{
|
||||
Item item = new Item();
|
||||
Item serverItem = new Item();
|
||||
if (armor[i - 48] != null && armor[i - 48].netID != 0)
|
||||
var index = i - (NetItem.maxNetInventory - (NetItem.armorSlots + NetItem.dyeSlots));
|
||||
if (armor[index] != null && armor[index].netID != 0)
|
||||
{
|
||||
if (playerData.inventory[i].netID != armor[i - 48].netID)
|
||||
if (playerData.inventory[i].netID != armor[index].netID)
|
||||
{
|
||||
item.netDefaults(armor[i - 48].netID);
|
||||
item.Prefix(armor[i - 48].prefix);
|
||||
item.netDefaults(armor[index].netID);
|
||||
item.Prefix(armor[index].prefix);
|
||||
item.AffixName();
|
||||
player.SendMessage(player.IgnoreActionsForInventory = "Your armor (" + item.name + ") needs to be deleted.",
|
||||
Color.Cyan);
|
||||
check = false;
|
||||
}
|
||||
else if (playerData.inventory[i].prefix != armor[i - 48].prefix)
|
||||
else if (playerData.inventory[i].prefix != armor[index].prefix)
|
||||
{
|
||||
item.netDefaults(armor[i - 48].netID);
|
||||
item.Prefix(armor[i - 48].prefix);
|
||||
item.netDefaults(armor[index].netID);
|
||||
item.Prefix(armor[index].prefix);
|
||||
item.AffixName();
|
||||
player.SendMessage(player.IgnoreActionsForInventory = "Your armor (" + item.name + ") needs to be deleted.",
|
||||
Color.Cyan);
|
||||
check = false;
|
||||
}
|
||||
else if (armor[i - 48].stack > playerData.inventory[i].stack)
|
||||
else if (armor[index].stack > playerData.inventory[i].stack)
|
||||
{
|
||||
item.netDefaults(armor[i - 48].netID);
|
||||
item.Prefix(armor[i - 48].prefix);
|
||||
item.netDefaults(armor[index].netID);
|
||||
item.Prefix(armor[index].prefix);
|
||||
item.AffixName();
|
||||
player.SendMessage(
|
||||
player.IgnoreActionsForInventory =
|
||||
|
|
@ -1629,6 +1694,44 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
}
|
||||
else if(i < (NetItem.maxNetInventory - (NetItem.armorSlots + NetItem.dyeSlots)))
|
||||
{
|
||||
Item item = new Item();
|
||||
Item serverItem = new Item();
|
||||
var index = i - (NetItem.maxNetInventory - NetItem.dyeSlots);
|
||||
if (dye[index] != null && dye[index].netID != 0)
|
||||
{
|
||||
if (playerData.inventory[i].netID != dye[index].netID)
|
||||
{
|
||||
item.netDefaults(dye[index].netID);
|
||||
item.Prefix(dye[index].prefix);
|
||||
item.AffixName();
|
||||
player.SendMessage(player.IgnoreActionsForInventory = "Your dye (" + item.name + ") needs to be deleted.",
|
||||
Color.Cyan);
|
||||
check = false;
|
||||
}
|
||||
else if (playerData.inventory[i].prefix != dye[index].prefix)
|
||||
{
|
||||
item.netDefaults(dye[index].netID);
|
||||
item.Prefix(dye[index].prefix);
|
||||
item.AffixName();
|
||||
player.SendMessage(player.IgnoreActionsForInventory = "Your dye (" + item.name + ") needs to be deleted.",
|
||||
Color.Cyan);
|
||||
check = false;
|
||||
}
|
||||
else if (dye[index].stack > playerData.inventory[i].stack)
|
||||
{
|
||||
item.netDefaults(dye[index].netID);
|
||||
item.Prefix(dye[index].prefix);
|
||||
item.AffixName();
|
||||
player.SendMessage(
|
||||
player.IgnoreActionsForInventory =
|
||||
"Your dye (" + item.name + ") (" + inventory[i].stack + ") needs to have its stack size decreased to (" +
|
||||
playerData.inventory[i].stack + ").", Color.Cyan);
|
||||
check = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return check;
|
||||
|
|
@ -1636,20 +1739,7 @@ namespace TShockAPI
|
|||
|
||||
public static bool CheckIgnores(TSPlayer player)
|
||||
{
|
||||
bool check = false;
|
||||
if (Config.PvPMode == "always" && !player.TPlayer.hostile)
|
||||
check = true;
|
||||
if (player.IgnoreActionsForInventory != "none")
|
||||
check = true;
|
||||
if (player.IgnoreActionsForCheating != "none")
|
||||
check = true;
|
||||
if (player.IgnoreActionsForDisabledArmor != "none")
|
||||
check = true;
|
||||
if (player.IgnoreActionsForClearingTrashCan)
|
||||
check = true;
|
||||
if (!player.IsLoggedIn && Config.RequireLogin)
|
||||
check = true;
|
||||
return check;
|
||||
return Config.PvPMode == "always" && !player.TPlayer.hostile || player.IgnoreActionsForInventory != "none" || player.IgnoreActionsForCheating != "none" || player.IgnoreActionsForDisabledArmor != "none" || player.IgnoreActionsForClearingTrashCan || !player.IsLoggedIn && Config.RequireLogin;;
|
||||
}
|
||||
|
||||
public void OnConfigRead(ConfigFile file)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DocumentationFile>bin\Debug\TShockAPI.XML</DocumentationFile>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
|
|
@ -67,7 +68,7 @@
|
|||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="TerrariaServer, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<Reference Include="TerrariaServer, Version=1.14.0.0, Culture=neutral, processorArchitecture=x86">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<ExecutableExtension>.exe</ExecutableExtension>
|
||||
<HintPath>..\TerrariaServerBins\TerrariaServer.exe</HintPath>
|
||||
|
|
@ -79,9 +80,6 @@
|
|||
<Compile Include="Hooks\GeneralHooks.cs" />
|
||||
<Compile Include="Hooks\PlayerHooks.cs" />
|
||||
<Compile Include="PaginationTools.cs" />
|
||||
<Compile Include="PluginUpdater\PluginUpdaterThread.cs" />
|
||||
<Compile Include="PluginUpdater\PluginVersionCheck.cs" />
|
||||
<Compile Include="PluginUpdater\VersionInfo.cs" />
|
||||
<Compile Include="Rest\RestPermissions.cs" />
|
||||
<Compile Include="SaveManager.cs" />
|
||||
<Compile Include="DB\BanManager.cs" />
|
||||
|
|
|
|||
|
|
@ -329,7 +329,7 @@ namespace TShockAPI
|
|||
/// <returns>The state of the tile</returns>
|
||||
private bool TileClear(int tileX, int tileY)
|
||||
{
|
||||
return !Main.tile[tileX, tileY].active;
|
||||
return !Main.tile[tileX, tileY].active();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -371,7 +371,7 @@ namespace TShockAPI
|
|||
var found = new List<Item>();
|
||||
Item item = new Item();
|
||||
string nameLower = name.ToLower();
|
||||
for (int i = -24; i < Main.maxItemTypes; i++)
|
||||
for (int i = -48; i < Main.maxItemTypes; i++)
|
||||
{
|
||||
item.netDefaults(i);
|
||||
if (item.name.ToLower() == nameLower)
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue