Merge branch 'general-devel' into rest-permissions

This commit is contained in:
Chris 2021-04-22 16:52:55 +09:30 committed by GitHub
commit d92bfc0f65
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 620 additions and 158 deletions

View file

@ -13,11 +13,38 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin
* If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change.
## Upcoming changes
* This could be you!
## TShock 4.5.2
* Added preliminary support for Terraria 1.4.2.2. (@hakusaro)
* Removed `/ungodme` and godmode warning (no longer necessary). Also, godmode now supports silent commands. (@hakusaro)
## TShock 4.5.1
* Fixed server crash from `/v2/players/list` & other parameterised REST endpoints. (@QuiCM, reported by @ATFGK)
* Added handling to the PlayerChat hook event. (@QuiCM - Thanks for the suggestion @Arthri)
* Changed the spawnboss command to support silent command specifiers. (@QuiCM, suggested by @nojomyth-dev)
* Updated /godmode to use Journey Mode's Godmode power instead of healing on damage. (requested by @tlworks, backported by @bartico6, implemented preemptive bugfix for creative powers mentioned by @Stealownz)
* Fixed /r attempting to send messages to players that have since disconnected. (@bartico6, reported by @Arthri)
* Added ban ticket ID to ban messages (@QuiCM, suggested by @Bippity)
* Refactored /wallow command. /reply no longer bypasses /wallow (@QuiCM)
## TShock 4.5.0.1
* Fixed conversion from old to new ban system for MySQL hosted ban databases. (@DeathCradle, @ATFGK)
* Fixed wrong identifier used for UUID bans. (@DeathCradle, @ATFGK)
* Fixed conversion from sqlite bans due to locking issue. (@DeathCradle, @Kojirremer)
## TShock 4.5.0
* Updated OTAPI and TSAPI to Terraria 1.4.2.1. (@Stealownz, @DeathCradle)
* Updated TShock with preliminary protocol support for Terraria 1.4.2.1. (@Stealownz)
## TShock 4.4.0 (Pre-release 16)
* Patched protocol issue. Thanks to Off (@tlworks) and @bartico6 for contributions, including packet captures, packet analysis, exploit proof-of-concept testing, patch testing, and detailed reproduction steps. (@hakusaro)
* Disabled debug by default. (@hakusaro)
* Changed "WinVer" field in `/serverinfo` to "Operating System". (@Terrabade)
* Rewritten `/grow`, added every default tree type & changed the default help response. (@Nova4334)
* Added a new permission: `tshock.world.growevil` to prevent players to grow evil biome trees, these trees spawn with evil biome blocks below them.
* Introduced `/wallow` to disable or enable recieving whispers from other players. (@Nova4334)
* Removed stoned & webbed from disabled status (@QuiCM)
* Fix -forceupdate flag not forcing updates (@Quake)
## TShock 4.4.0 (Pre-release 15)
* Overhauled Bans system. Bans are now based on 'identifiers'. (@QuiCM)

View file

@ -303,6 +303,15 @@ namespace TShockAPI
args.Handled = true;
return;
}
if (selectedItem.placeStyle != style)
{
TShock.Log.ConsoleError(string.Format("Bouncer / OnTileEdit rejected from (placestyle) {0} {1} {2} placeStyle: {3} expectedStyle: {4}",
args.Player.Name, action, editData, style, selectedItem.placeStyle));
args.Player.SendTileSquare(tileX, tileY, 1);
args.Handled = true;
return;
}
}
if (action == EditAction.KillTile && !Main.tileCut[tile.type] && !breakableTiles.Contains(tile.type))
@ -354,9 +363,9 @@ namespace TShockAPI
}
else if (CoilTileIds.Contains(editData))
{
/// Handle placement if the user is placing rope that comes from a ropecoil,
/// but have not created the ropecoil projectile recently or the projectile was not at the correct coordinate, or the tile that the projectile places does not match the rope it is suposed to place
/// projectile should be the same X coordinate as all tile places (Note by @Olink)
// Handle placement if the user is placing rope that comes from a ropecoil,
// but have not created the ropecoil projectile recently or the projectile was not at the correct coordinate, or the tile that the projectile places does not match the rope it is suposed to place
// projectile should be the same X coordinate as all tile places (Note by @Olink)
if (ropeCoilPlacements.ContainsKey(selectedItem.netID) &&
!args.Player.RecentlyCreatedProjectiles.Any(p => GetDataHandlers.projectileCreatesTile.ContainsKey(p.Type) && GetDataHandlers.projectileCreatesTile[p.Type] == editData &&
!p.Killed && Math.Abs((int)(Main.projectile[p.Index].position.X / 16f) - tileX) <= Math.Abs(Main.projectile[p.Index].velocity.X)))
@ -1106,6 +1115,7 @@ namespace TShockAPI
int tileX = args.TileX;
int tileY = args.TileY;
int flag = args.Flag;
short style = args.Style;
if (!TShock.Utils.TilePlacementValid(tileX, tileY) || (args.Player.Dead && TShock.Config.Settings.PreventDeadModification))
{
@ -1122,6 +1132,14 @@ namespace TShockAPI
return;
}
if (args.Player.SelectedItem.placeStyle != style)
{
TShock.Log.ConsoleError(string.Format("Bouncer / OnPlaceChest / rejected from invalid place style from {0}", args.Player.Name));
args.Player.SendTileSquare(tileX, tileY, 3);
args.Handled = true;
return;
}
if (flag != 0 && flag != 4 // if no container or container2 placement
&& Main.tile[tileX, tileY].type != TileID.Containers
&& Main.tile[tileX, tileY].type != TileID.Dressers
@ -1630,8 +1648,6 @@ namespace TShockAPI
short y = args.Y;
short type = args.Type;
short style = args.Style;
byte alternate = args.Alternate;
bool direction = args.Direction;
if (type < 0 || type >= Main.maxTileSets)
{
@ -1654,6 +1670,13 @@ namespace TShockAPI
return;
}
if (args.Player.SelectedItem.placeStyle != style)
{
TShock.Log.ConsoleError(string.Format("Bouncer / OnPlaceObject rejected object placement with invalid style from {0}", args.Player.Name));
args.Handled = true;
return;
}
//style 52 and 53 are used by ItemID.Fake_newchest1 and ItemID.Fake_newchest2
//These two items cause localised lag and rendering issues
if (type == TileID.FakeContainers && (style == 52 || style == 53))

View file

@ -37,6 +37,7 @@ using OTAPI.Tile;
using TShockAPI.Localization;
using System.Text.RegularExpressions;
using Terraria.DataStructures;
using Terraria.GameContent.Creative;
namespace TShockAPI
{
@ -585,6 +586,11 @@ namespace TShockAPI
{
HelpText = "Sends a PM to a player."
});
add(new Command(Permissions.whisper, Wallow, "wallow")
{
AllowServer = false,
HelpText = "Toggles to either ignore or recieve whispers from other players."
});
add(new Command(Permissions.createdumps, CreateDumps, "dump-reference-data")
{
HelpText = "Creates a reference tables for Terraria data types and the TShock permission system in the server folder."
@ -1148,7 +1154,7 @@ namespace TShockAPI
args.Player.SendInfoMessage("Memory usage: " + Process.GetCurrentProcess().WorkingSet64);
args.Player.SendInfoMessage("Allocated memory: " + Process.GetCurrentProcess().VirtualMemorySize64);
args.Player.SendInfoMessage("Total processor time: " + Process.GetCurrentProcess().TotalProcessorTime);
args.Player.SendInfoMessage("WinVer: " + Environment.OSVersion);
args.Player.SendInfoMessage("Operating system: " + Environment.OSVersion);
args.Player.SendInfoMessage("Proc count: " + Environment.ProcessorCount);
args.Player.SendInfoMessage("Machine name: " + Environment.MachineName);
}
@ -1517,7 +1523,7 @@ namespace TShockAPI
if (banResult?.Ban != null)
{
player.Disconnect($"You have been banned: {banResult.Ban.Reason}.");
player.Disconnect($"#{banResult.Ban.TicketNumber} - You have been banned: {banResult.Ban.Reason}.");
}
}
@ -2421,6 +2427,8 @@ namespace TShockAPI
return;
}
string message = "{0} spawned {1} {2} time(s)";
string spawnName;
NPC npc = new NPC();
switch (args.Parameters[0].ToLower())
{
@ -2433,87 +2441,89 @@ namespace TShockAPI
npc.SetDefaults(i);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
}
TSPlayer.All.SendSuccessMessage("{0} has spawned all bosses {1} time(s).", args.Player.Name, amount);
return;
spawnName = "all bosses";
break;
case "brain":
case "brain of cthulhu":
case "boc":
npc.SetDefaults(266);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned the Brain of Cthulhu {1} time(s).", args.Player.Name, amount);
return;
spawnName = "the Brain of Cthulhu";
break;
case "destroyer":
npc.SetDefaults(134);
TSPlayer.Server.SetTime(false, 0.0);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned the Destroyer {1} time(s).", args.Player.Name, amount);
return;
spawnName = "the Destroyer";
break;
case "duke":
case "duke fishron":
case "fishron":
npc.SetDefaults(370);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned Duke Fishron {1} time(s).", args.Player.Name, amount);
return;
spawnName = "Duke Fishron";
break;
case "eater":
case "eater of worlds":
case "eow":
npc.SetDefaults(13);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned the Eater of Worlds {1} time(s).", args.Player.Name, amount);
return;
spawnName = "the Eater of Worlds";
break;
case "eye":
case "eye of cthulhu":
case "eoc":
npc.SetDefaults(4);
TSPlayer.Server.SetTime(false, 0.0);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned the Eye of Cthulhu {1} time(s).", args.Player.Name, amount);
return;
spawnName = "the Eye of Cthulhu";
break;
case "golem":
npc.SetDefaults(245);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned Golem {1} time(s).", args.Player.Name, amount);
return;
spawnName = "the Golem";
break;
case "king":
case "king slime":
case "ks":
npc.SetDefaults(50);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned King Slime {1} time(s).", args.Player.Name, amount);
return;
spawnName = "the King Slime";
break;
case "plantera":
npc.SetDefaults(262);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned Plantera {1} time(s).", args.Player.Name, amount);
return;
spawnName = "Plantera";
break;
case "prime":
case "skeletron prime":
npc.SetDefaults(127);
TSPlayer.Server.SetTime(false, 0.0);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned Skeletron Prime {1} time(s).", args.Player.Name, amount);
return;
spawnName = "Skeletron Prime";
break;
case "queen bee":
case "qb":
npc.SetDefaults(222);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned Queen Bee {1} time(s).", args.Player.Name, amount);
return;
spawnName = "the Queen Bee";
break;
case "skeletron":
npc.SetDefaults(35);
TSPlayer.Server.SetTime(false, 0.0);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned Skeletron {1} time(s).", args.Player.Name, amount);
return;
spawnName = "Skeletron";
break;
case "twins":
TSPlayer.Server.SetTime(false, 0.0);
npc.SetDefaults(125);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
npc.SetDefaults(126);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned the Twins {1} time(s).", args.Player.Name, amount);
return;
spawnName = "the Twins";
break;
case "wof":
case "wall of flesh":
if (Main.wofNPCIndex != -1)
@ -2527,103 +2537,114 @@ namespace TShockAPI
return;
}
NPC.SpawnWOF(new Vector2(args.Player.X, args.Player.Y));
TSPlayer.All.SendSuccessMessage("{0} has spawned the Wall of Flesh.", args.Player.Name);
return;
spawnName = "the Wall of Flesh";
break;
case "moon":
case "moon lord":
case "ml":
npc.SetDefaults(398);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned the Moon Lord {1} time(s).", args.Player.Name, amount);
return;
spawnName = "the Moon Lord";
break;
case "empress":
case "empress of light":
case "eol":
npc.SetDefaults(636);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned the Empress of Light {1} time(s).", args.Player.Name, amount);
return;
spawnName = "the Empress of Light";
break;
case "queen slime":
case "qs":
npc.SetDefaults(657);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned Queen Slime {1} time(s).", args.Player.Name, amount);
return;
spawnName = "the Queen Slime";
break;
case "lunatic":
case "lunatic cultist":
case "cultist":
case "lc":
npc.SetDefaults(439);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned the Lunatic Cultist {1} time(s).", args.Player.Name, amount);
return;
spawnName = "the Lunatic Cultist";
break;
case "betsy":
npc.SetDefaults(551);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned Betsy {1} time(s).", args.Player.Name, amount);
return;
spawnName = "Betsy";
break;
case "flying dutchman":
case "flying":
case "dutchman":
npc.SetDefaults(491);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned the Flying Dutchman {1} time(s).", args.Player.Name, amount);
return;
spawnName = "the Flying Dutchman";
break;
case "mourning wood":
npc.SetDefaults(325);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned Mourning Wood {1} time(s).", args.Player.Name, amount);
return;
spawnName = "Mourning Wood";
break;
case "pumpking":
npc.SetDefaults(327);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned Pumpking {1} time(s).", args.Player.Name, amount);
return;
spawnName = "the Pumpking";
break;
case "everscream":
npc.SetDefaults(344);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned Everscream {1} time(s).", args.Player.Name, amount);
return;
spawnName = "Everscream";
break;
case "santa-nk1":
case "santa":
npc.SetDefaults(346);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned Santa-NK1 {1} time(s).", args.Player.Name, amount);
return;
spawnName = "Santa-NK1";
break;
case "ice queen":
npc.SetDefaults(345);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned the Ice Queen {1} time(s).", args.Player.Name, amount);
return;
spawnName = "the Ice Queen";
break;
case "martian saucer":
npc.SetDefaults(392);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned the Martian Saucer {1} time(s).", args.Player.Name, amount);
return;
spawnName = "a Martian Saucer";
break;
case "solar pillar":
npc.SetDefaults(517);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned the Solar Pillar {1} time(s).", args.Player.Name, amount);
return;
spawnName = "a Solar Pillar";
break;
case "nebula pillar":
npc.SetDefaults(507);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned the Nebula Pillar {1} time(s).", args.Player.Name, amount);
return;
spawnName = "a Nebula Pillar";
break;
case "vortex pillar":
npc.SetDefaults(422);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned the Vortex Pillar {1} time(s).", args.Player.Name, amount);
return;
spawnName = "a Vortex Pillar";
break;
case "stardust pillar":
npc.SetDefaults(493);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
TSPlayer.All.SendSuccessMessage("{0} has spawned the Stardust Pillar {1} time(s).", args.Player.Name, amount);
return;
spawnName = "a Stardust Pillar";
break;
default:
args.Player.SendErrorMessage("Invalid boss type!");
return;
}
if (args.Silent)
{
//"You spawned <spawn name> <x> time(s)"
args.Player.SendSuccessMessage(message, "You", spawnName, amount);
}
else
{
//"<player> spawned <spawn name> <x> time(s)"
TSPlayer.All.SendSuccessMessage(message, args.Player.Name, spawnName, amount);
}
}
private static void SpawnMob(CommandArgs args)
@ -5283,14 +5304,13 @@ namespace TShockAPI
args.Player.SendFileTextAsMessage(FileTools.RulesPath);
}
private static void Whisper(CommandArgs args)
public static void Whisper(CommandArgs args)
{
if (args.Parameters.Count < 2)
{
args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}whisper <player> <text>", Specifier);
args.Player.SendErrorMessage("Invalid syntax! Proper usage: /whisper <player> <text>");
return;
}
var players = TSPlayer.FindByNameOrID(args.Parameters[0]);
if (players.Count == 0)
{
@ -5307,6 +5327,11 @@ namespace TShockAPI
else
{
var plr = players[0];
if (!plr.AcceptingWhispers)
{
args.Player.SendErrorMessage("This player is not accepting whispers.");
return;
}
var msg = string.Join(" ", args.Parameters.ToArray(), 1, args.Parameters.Count - 1);
plr.SendMessage(String.Format("<From {0}> {1}", args.Player.Name, msg), Color.MediumPurple);
args.Player.SendMessage(String.Format("<To {0}> {1}", plr.Name, msg), Color.MediumPurple);
@ -5315,18 +5340,34 @@ namespace TShockAPI
}
}
private static void Wallow(CommandArgs args)
{
args.Player.AcceptingWhispers = !args.Player.AcceptingWhispers;
args.Player.SendSuccessMessage($"You {(args.Player.AcceptingWhispers ? "may now" : "will no longer")} receive whispers from other players.");
args.Player.SendSuccessMessage($"You can toggle this with the '{Specifier}wallow' command.");
}
private static void Reply(CommandArgs args)
{
if (args.Player.mute)
{
args.Player.SendErrorMessage("You are muted.");
}
else if (args.Player.LastWhisper != null)
else if (args.Player.LastWhisper != null && args.Player.LastWhisper.Active)
{
if (!args.Player.LastWhisper.AcceptingWhispers)
{
args.Player.SendErrorMessage("This player is not accepting whispers.");
return;
}
var msg = string.Join(" ", args.Parameters);
args.Player.LastWhisper.SendMessage(String.Format("<From {0}> {1}", args.Player.Name, msg), Color.MediumPurple);
args.Player.SendMessage(String.Format("<To {0}> {1}", args.Player.LastWhisper.Name, msg), Color.MediumPurple);
}
else if (args.Player.LastWhisper != null)
{
args.Player.SendErrorMessage("The player you're attempting to reply to is no longer online.");
}
else
{
args.Player.SendErrorMessage("You haven't previously received any whispers. Please use {0}whisper to whisper to other people.", Specifier);
@ -5410,7 +5451,7 @@ namespace TShockAPI
type = 170;
}
var ply = players[0];
int p = Projectile.NewProjectile(ply.TPlayer.position.X, ply.TPlayer.position.Y - 64f, 0f, -8f, type, 0, (float)0);
int p = Projectile.NewProjectile(Projectile.GetNoneSource(), ply.TPlayer.position.X, ply.TPlayer.position.Y - 64f, 0f, -8f, type, 0, (float)0);
Main.projectile[p].Kill();
args.Player.SendSuccessMessage("Launched Firework on {0}.", ply.Name);
}
@ -6010,13 +6051,11 @@ namespace TShockAPI
}
}
private static void Grow(CommandArgs args)
public static void Grow(CommandArgs args)
{
if (args.Parameters.Count != 1)
{
args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}grow <tree/epictree/mushroom/cactus/herb>", Specifier);
return;
}
bool growevilAmb = args.Player.HasPermission(Permissions.growevil);
string subcmd = args.Parameters.Count == 0 ? "help" : args.Parameters[0].ToLower();
var name = "Fail";
var x = args.Player.TileX;
var y = args.Player.TileY + 3;
@ -6027,10 +6066,37 @@ namespace TShockAPI
return;
}
switch (args.Parameters[0].ToLower())
switch (subcmd)
{
case "tree":
for (int i = x - 1; i < x + 2; i++)
case "help":
{
if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out int pageNumber))
return;
var lines = new List<string>
{
"- Default trees :",
" 'basic', 'sakura', 'willow', 'boreal', 'mahogany', 'ebonwood', 'shadewood', 'pearlwood'.",
"- Palm trees :",
" 'palm', 'corruptpalm', 'crimsonpalm', 'hallowpalm'.",
"- Gem trees :",
" 'topaz', 'amethyst', 'sapphire', 'emerald', 'ruby', 'diamond', 'amber'.",
"- Misc :",
" 'cactus', 'herb', 'mushroom'."
};
PaginationTools.SendPage(args.Player, pageNumber, lines,
new PaginationTools.Settings
{
HeaderFormat = "Trees types & misc available to use. ({0}/{1}):",
FooterFormat = "Type {0}grow help {{0}} for more sub-commands.".SFormat(Commands.Specifier)
}
);
}
break;
case "basic":
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 2;
@ -6038,37 +6104,279 @@ namespace TShockAPI
}
Main.tile[x, y - 1].wall = 0;
WorldGen.GrowTree(x, y);
name = "Tree";
name = "Basic Tree";
break;
case "epictree":
for (int i = x - 1; i < x + 2; i++)
case "boreal":
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 147;
Main.tile[i, y].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.GrowTree(x, y);
name = "Boreal Tree";
break;
case "mahogany":
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 60;
Main.tile[i, y].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.GrowTree(x, y);
name = "Rich Mahogany";
break;
case "sakura":
for (int i = x - 2; i < x + 3; i++)
{
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);
WorldGen.GrowEpicTree(x, y);
name = "Epic Tree";
WorldGen.TryGrowingTreeByType(596, x, y);
name = "Sakura Tree";
break;
case "mushroom":
for (int i = x - 1; i < x + 2; i++)
case "willow":
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 70;
Main.tile[i, y].type = 2;
Main.tile[i, y].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.GrowShroom(x, y);
name = "Mushroom";
WorldGen.TryGrowingTreeByType(616, x, y);
name = "Willow Tree";
break;
case "shadewood":
if (growevilAmb)
{
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 199;
Main.tile[i, y].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.GrowTree(x, y);
name = "Shadewood tree";
}
else args.Player.SendErrorMessage("You do not have permission to grow this tree type");
break;
case "ebonwood":
if (growevilAmb)
{
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 23;
Main.tile[i, y].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.GrowTree(x, y);
name = "Ebonwood Tree";
}
else args.Player.SendErrorMessage("You do not have permission to grow this tree type");
break;
case "pearlwood":
if (growevilAmb)
{
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 109;
Main.tile[i, y].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.GrowTree(x, y);
name = "Pearlwood Tree";
}
else args.Player.SendErrorMessage("You do not have permission to grow this tree type");
break;
case "palm":
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 53;
Main.tile[i, y].wall = 0;
}
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y + 1].active(true);
Main.tile[i, y + 1].type = 397;
Main.tile[i, y + 1].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.GrowPalmTree(x, y);
name = "Desert Palm";
break;
case "hallowpalm":
if (growevilAmb)
{
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 116;
Main.tile[i, y].wall = 0;
}
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y + 1].active(true);
Main.tile[i, y + 1].type = 402;
Main.tile[i, y + 1].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.GrowPalmTree(x, y);
name = "Hallow Palm";
}
else args.Player.SendErrorMessage("You do not have permission to grow this tree type");
break;
case "crimsonpalm":
if (growevilAmb)
{
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 234;
Main.tile[i, y].wall = 0;
}
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y + 1].active(true);
Main.tile[i, y + 1].type = 399;
Main.tile[i, y + 1].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.GrowPalmTree(x, y);
name = "Crimson Palm";
}
else args.Player.SendErrorMessage("You do not have permission to grow this tree type");
break;
case "corruptpalm":
if (growevilAmb)
{
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 112;
Main.tile[i, y].wall = 0;
}
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y + 1].active(true);
Main.tile[i, y + 1].type = 398;
Main.tile[i, y + 1].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.GrowPalmTree(x, y);
name = "Corruption Palm";
}
else args.Player.SendErrorMessage("You do not have permission to grow this tree type");
break;
case "topaz":
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 1;
Main.tile[i, y].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.TryGrowingTreeByType(583, x, y);
name = "Topaz Gemtree";
break;
case "amethyst":
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 1;
Main.tile[i, y].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.TryGrowingTreeByType(584, x, y);
name = "Amethust Gemtree";
break;
case "sapphire":
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 1;
Main.tile[i, y].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.TryGrowingTreeByType(585, x, y);
name = "Sapphire Gemtree";
break;
case "emerald":
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 1;
Main.tile[i, y].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.TryGrowingTreeByType(586, x, y);
name = "Emerald Gemtree";
break;
case "ruby":
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 1;
Main.tile[i, y].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.TryGrowingTreeByType(587, x, y);
name = "Ruby Gemtree";
break;
case "diamond":
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 1;
Main.tile[i, y].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.TryGrowingTreeByType(588, x, y);
name = "Diamond Gemtree";
break;
case "amber":
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 1;
Main.tile[i, y].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.TryGrowingTreeByType(589, x, y);
name = "Amber Gemtree";
break;
case "cactus":
Main.tile[x, y].type = 53;
WorldGen.GrowCactus(x, y);
name = "Cactus";
break;
case "herb":
Main.tile[x, y].active(true);
Main.tile[x, y].frameX = 36;
@ -6076,13 +6384,29 @@ namespace TShockAPI
WorldGen.GrowAlch(x, y);
name = "Herb";
break;
case "mushroom":
for (int i = x - 2; i < x + 3; i++)
{
Main.tile[i, y].active(true);
Main.tile[i, y].type = 70;
Main.tile[i, y].wall = 0;
}
Main.tile[x, y - 1].wall = 0;
WorldGen.GrowShroom(x, y);
name = "Glowing Mushroom Tree";
break;
default:
args.Player.SendErrorMessage("Unknown plant!");
return;
}
args.Player.SendTileSquare(x, y);
if (args.Parameters.Count == 1)
{
args.Player.SendTileSquare(x - 2, y - 20, 25);
args.Player.SendSuccessMessage("Tried to grow a " + name + ".");
}
}
private static void ToggleGodMode(CommandArgs args)
{
@ -6123,14 +6447,18 @@ namespace TShockAPI
playerToGod.GodMode = !playerToGod.GodMode;
if (playerToGod == args.Player)
{
args.Player.SendSuccessMessage(string.Format("You are {0} in god mode.", args.Player.GodMode ? "now" : "no longer"));
}
else
var godPower = CreativePowerManager.Instance.GetPower<CreativePowers.GodmodePower>();
godPower.SetEnabledState(playerToGod.Index, playerToGod.GodMode);
if (playerToGod != args.Player)
{
args.Player.SendSuccessMessage(string.Format("{0} is {1} in god mode.", playerToGod.Name, playerToGod.GodMode ? "now" : "no longer"));
playerToGod.SendSuccessMessage(string.Format("You are {0} in god mode.", playerToGod.GodMode ? "now" : "no longer"));
}
if (!args.Silent || (playerToGod == args.Player))
{
playerToGod.SendSuccessMessage(string.Format("You are {0} in god mode.", args.Player.GodMode ? "now" : "no longer"));
}
}

View file

@ -37,18 +37,7 @@ namespace TShockAPI.DB
/// <summary>
/// Readonly dictionary of Bans, keyed on ban ticket number.
/// </summary>
public ReadOnlyDictionary<int, Ban> Bans
{
get
{
if (_bans == null)
{
_bans = RetrieveAllBans().ToDictionary(b => b.TicketNumber);
}
return new ReadOnlyDictionary<int, Ban>(_bans);
}
}
public ReadOnlyDictionary<int, Ban> Bans => new ReadOnlyDictionary<int, Ban>(_bans);
/// <summary>
/// Event invoked when a ban is checked for validity
@ -93,12 +82,24 @@ namespace TShockAPI.DB
throw new Exception("Could not find a database library (probably Sqlite3.dll)");
}
EnsureBansCollection();
TryConvertBans();
OnBanValidate += BanValidateCheck;
OnBanPreAdd += BanAddedCheck;
}
/// <summary>
/// Ensures the <see cref="_bans"/> collection is ready to use.
/// </summary>
private void EnsureBansCollection()
{
if (_bans == null)
{
_bans = RetrieveAllBans().ToDictionary(b => b.TicketNumber);
}
}
/// <summary>
/// Converts bans from the old ban system to the new.
/// </summary>
@ -107,7 +108,7 @@ namespace TShockAPI.DB
int res;
if (database.GetSqlType() == SqlType.Mysql)
{
res = database.QueryScalar<int>("SELECT COUNT(name) FROM information_schema.tables WHERE table_schema = @0 and table_name = 'Bans'", TShock.Config.Settings.MySqlDbName);
res = database.QueryScalar<int>("SELECT COUNT(table_name) FROM information_schema.tables WHERE table_schema = @0 and table_name = 'Bans'", TShock.Config.Settings.MySqlDbName);
}
else
{
@ -116,6 +117,7 @@ namespace TShockAPI.DB
if (res != 0)
{
var bans = new List<BanPreAddEventArgs>();
using (var reader = database.QueryReader("SELECT * FROM Bans"))
{
while (reader.Read())
@ -140,22 +142,46 @@ namespace TShockAPI.DB
if (!string.IsNullOrWhiteSpace(ip))
{
InsertBan($"{Identifier.IP}{ip}", reason, banningUser, start, end);
bans.Add(new BanPreAddEventArgs
{
Identifier = $"{Identifier.IP}{ip}",
Reason = reason,
BanningUser = banningUser,
BanDateTime = start,
ExpirationDateTime = end
});
}
if (!string.IsNullOrWhiteSpace(account))
{
InsertBan($"{Identifier.Account}{account}", reason, banningUser, start, end);
bans.Add(new BanPreAddEventArgs
{
Identifier = $"{Identifier.Account}{account}",
Reason = reason,
BanningUser = banningUser,
BanDateTime = start,
ExpirationDateTime = end
});
}
if (!string.IsNullOrWhiteSpace(uuid))
{
InsertBan($"{Identifier.UUID}{uuid}", reason, banningUser, start, end);
bans.Add(new BanPreAddEventArgs
{
Identifier = $"{Identifier.UUID}{uuid}",
Reason = reason,
BanningUser = banningUser,
BanDateTime = start,
ExpirationDateTime = end
});
}
}
}
database.Query("DROP TABLE 'Bans'");
foreach (var ban in bans)
InsertBan(ban);
database.Query("DROP TABLE Bans");
}
}
@ -179,12 +205,12 @@ namespace TShockAPI.DB
{
if (ban.ExpirationDateTime == DateTime.MaxValue)
{
player.Disconnect("You are banned: " + ban.Reason);
player.Disconnect($"#{ban.TicketNumber} - You are banned: {ban.Reason}");
return true;
}
TimeSpan ts = ban.ExpirationDateTime - DateTime.UtcNow;
player.Disconnect($"You are banned: {ban.Reason} ({ban.GetPrettyExpirationString()} remaining)");
player.Disconnect($"#{ban.TicketNumber} - You are banned: {ban.Reason} ({ban.GetPrettyExpirationString()} remaining)");
return true;
}
@ -252,7 +278,16 @@ namespace TShockAPI.DB
BanDateTime = fromDate,
ExpirationDateTime = toDate
};
return InsertBan(args);
}
/// <summary>
/// Adds a new ban for the given data. Returns a Ban object if the ban was added, else null
/// </summary>
/// <param name="args">A predefined instance of <see cref="BanPreAddEventArgs"/></param>
/// <returns></returns>
public AddBanResult InsertBan(BanPreAddEventArgs args)
{
OnBanPreAdd?.Invoke(this, args);
if (!args.Valid)
@ -265,21 +300,21 @@ namespace TShockAPI.DB
if (database.GetSqlType() == SqlType.Mysql)
{
query += "SELECT CAST(LAST_INSERT_ID() as INT);";
query += "SELECT LAST_INSERT_ID();";
}
else
{
query += "SELECT CAST(last_insert_rowid() as INT);";
}
int ticketId = database.QueryScalar<int>(query, identifier, reason, banningUser, fromDate.Ticks, toDate.Ticks);
int ticketId = database.QueryScalar<int>(query, args.Identifier, args.Reason, args.BanningUser, args.BanDateTime.Ticks, args.ExpirationDateTime.Ticks);
if (ticketId == 0)
{
return new AddBanResult { Message = "Database insert failed." };
}
Ban b = new Ban(ticketId, identifier, reason, banningUser, fromDate, toDate);
Ban b = new Ban(ticketId, args.Identifier, args.Reason, args.BanningUser, args.BanDateTime, args.ExpirationDateTime);
_bans.Add(ticketId, b);
OnBanPostAdd?.Invoke(this, new BanEventArgs { Ban = b });

View file

@ -939,12 +939,16 @@ namespace TShockAPI
/// The Y coordinate
/// </summary>
public int TileY { get; set; }
/// <summary>
/// Place style used
/// </summary>
public short Style { get; set; }
}
/// <summary>
/// When a chest is added or removed from the world.
/// </summary>
public static HandlerList<PlaceChestEventArgs> PlaceChest = new HandlerList<PlaceChestEventArgs>();
private static bool OnPlaceChest(TSPlayer player, MemoryStream data, int flag, int tilex, int tiley)
private static bool OnPlaceChest(TSPlayer player, MemoryStream data, int flag, int tilex, int tiley, short style)
{
if (PlaceChest == null)
return false;
@ -956,6 +960,7 @@ namespace TShockAPI
Flag = flag,
TileX = tilex,
TileY = tiley,
Style = style
};
PlaceChest.Invoke(null, args);
return args.Handled;
@ -2571,10 +2576,6 @@ namespace TShockAPI
args.Player.PlayerData.maxHealth = max;
}
if (args.Player.GodMode && (cur < max))
{
args.Player.Heal(args.TPlayer.statLifeMax2);
}
return false;
}
@ -2699,6 +2700,7 @@ namespace TShockAPI
float[] ai = new float[Projectile.maxAI];
for (int i = 0; i < Projectile.maxAI; ++i)
ai[i] = !bits.AI[i] ? 0.0f : args.Data.ReadSingle();
ushort bannerId = bits.HasBannerIdToRespondTo ? args.Data.ReadUInt16() : (ushort)0;
short dmg = bits.HasDamage ? args.Data.ReadInt16() : (short)0;
float knockback = bits.HasKnockback ? args.Data.ReadSingle() : 0.0f;
short origDmg = bits.HasOriginalDamage ? args.Data.ReadInt16() : (short)0;
@ -2884,9 +2886,9 @@ namespace TShockAPI
int flag = args.Data.ReadByte();
int tileX = args.Data.ReadInt16();
int tileY = args.Data.ReadInt16();
args.Data.ReadInt16(); // Ignore style
short style = args.Data.ReadInt16();
if (OnPlaceChest(args.Player, args.Data, flag, tileX, tileY))
if (OnPlaceChest(args.Player, args.Data, flag, tileX, tileY, style))
return true;
return false;
@ -3732,12 +3734,6 @@ namespace TShockAPI
if (OnPlayerDamage(args.Player, args.Data, id, direction, dmg, pvp, crit, playerDeathReason))
return true;
if (TShock.Players[id].GodMode)
{
TShock.Log.ConsoleDebug("GetDataHandlers / HandlePlayerDamageV2 rejected (god mode on) {0}", args.Player.Name);
TShock.Players[id].Heal(args.TPlayer.statLifeMax);
}
return false;
}

View file

@ -449,14 +449,16 @@ namespace TShockAPI.Hooks
/// <param name="ply">The player firing the event.</param>
/// <param name="rawtext">The raw chat text sent by the player.</param>
/// <param name="tshockText">The chat text after being formatted.</param>
public static void OnPlayerChat(TSPlayer ply, string rawtext, ref string tshockText)
public static bool OnPlayerChat(TSPlayer ply, string rawtext, ref string tshockText)
{
if (PlayerChat == null)
return;
return false;
var args = new PlayerChatEventArgs {Player = ply, RawText = rawtext, TShockFormattedText = tshockText};
PlayerChat(args);
tshockText = args.TShockFormattedText;
return args.Handled;
}
/// <summary>

View file

@ -38,6 +38,12 @@ namespace TShockAPI.Models.Projectiles
}
}
public bool HasBannerIdToRespondTo
{
get => bitsbyte[3];
set => bitsbyte[3] = value;
}
/// <summary>
/// Gets or Sets the Damage flag on the backing field
/// </summary>

View file

@ -319,6 +319,9 @@ namespace TShockAPI
[Description("User can grow plants.")]
public static readonly string grow = "tshock.world.grow";
[Description("User can grow evil biome plants.")]
public static readonly string growevil = "tshock.world.growevil";
[Description("User can change hardmode state.")]
public static readonly string hardmode = "tshock.world.hardmode";

View file

@ -53,5 +53,5 @@ using System.Runtime.InteropServices;
// Also, be sure to release on github with the exact assembly version tag as below
// so that the update manager works correctly (via the Github releases api and mimic)
[assembly: AssemblyVersion("4.4.0")]
[assembly: AssemblyFileVersion("4.4.0")]
[assembly: AssemblyVersion("4.5.2")]
[assembly: AssemblyFileVersion("4.5.2")]

View file

@ -94,17 +94,17 @@ namespace Rests
/// </summary>
/// <returns></returns>
public IEnumerator<EscapedParameter> GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
foreach (IParameter param in _collection)
{
yield return new EscapedParameter(param);
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
/// <summary>

View file

@ -599,6 +599,11 @@ namespace TShockAPI
public bool SilentJoinInProgress;
/// <summary>
/// Whether the player is accepting whispers from other users
/// </summary>
public bool AcceptingWhispers = true;
/// <summary>Checks if a player is in range of a given tile if range checks are enabled.</summary>
/// <param name="x"> The x coordinate of the tile.</param>
/// <param name="y">The y coordinate of the tile.</param>
@ -1557,8 +1562,6 @@ namespace TShockAPI
public virtual void Disable(string reason = "", DisableFlags flags = DisableFlags.WriteToLog)
{
LastThreat = DateTime.UtcNow;
SetBuff(BuffID.Frozen, 330, true);
SetBuff(BuffID.Stoned, 330, true);
SetBuff(BuffID.Webbed, 330, true);
if (ActiveChest != -1)
@ -1639,7 +1642,7 @@ namespace TShockAPI
if (force)
{
TShock.Bans.InsertBan($"{Identifier.IP}{IP}", reason, adminUserName, DateTime.UtcNow, DateTime.MaxValue);
TShock.Bans.InsertBan($"{Identifier.IP}{UUID}", reason, adminUserName, DateTime.UtcNow, DateTime.MaxValue);
TShock.Bans.InsertBan($"{Identifier.UUID}{UUID}", reason, adminUserName, DateTime.UtcNow, DateTime.MaxValue);
if (Account != null)
{
TShock.Bans.InsertBan($"{Identifier.Account}{Account.Name}", reason, adminUserName, DateTime.UtcNow, DateTime.MaxValue);

View file

@ -44,6 +44,7 @@ using TShockAPI.Sockets;
using TShockAPI.CLI;
using TShockAPI.Localization;
using TShockAPI.Configuration;
using Terraria.GameContent.Creative;
namespace TShockAPI
{
@ -57,7 +58,7 @@ namespace TShockAPI
/// <summary>VersionNum - The version number the TerrariaAPI will return back to the API. We just use the Assembly info.</summary>
public static readonly Version VersionNum = Assembly.GetExecutingAssembly().GetName().Version;
/// <summary>VersionCodename - The version codename is displayed when the server starts. Inspired by software codenames conventions.</summary>
public static readonly string VersionCodename = "Now with less velocity, thanks to Off + Quake. Usual thanks to Chris/White <3";
public static readonly string VersionCodename = "April Lyrids edition";
/// <summary>SavePath - This is the path TShock saves its data in. This path is relative to the TerrariaServer.exe (not in ServerPlugins).</summary>
public static string SavePath = "tshock";
@ -890,6 +891,13 @@ namespace TShockAPI
/// <param name="args">args - EventArgs args</param>
private void OnUpdate(EventArgs args)
{
// This forces Terraria to actually continue to update
// even if there are no clients connected
if (ServerApi.ForceUpdate)
{
Netplay.HasClients = true;
}
if (Backups.IsBackupTime)
Backups.Backup();
//call these every second, not every update
@ -1209,6 +1217,23 @@ namespace TShockAPI
Players[args.Who] = null;
//Reset toggle creative powers to default, preventing potential power transfer & desync on another user occupying this slot later.
foreach(var kv in CreativePowerManager.Instance._powersById)
{
var power = kv.Value;
//No need to reset sliders - those are reset manually by the game, most likely an oversight that toggles don't receive this treatment.
if (power is CreativePowers.APerPlayerTogglePower toggle)
{
if (toggle._perPlayerIsEnabled[args.Who] == toggle._defaultToggleState)
continue;
toggle.SetEnabledState(args.Who, toggle._defaultToggleState);
}
}
if (tsplr.ReceivedInfo)
{
if (!tsplr.SilentKickInProgress && tsplr.State >= 3)
@ -1323,9 +1348,17 @@ namespace TShockAPI
{
text = String.Format(Config.Settings.ChatFormat, tsplr.Group.Name, tsplr.Group.Prefix, tsplr.Name, tsplr.Group.Suffix,
args.Text);
Hooks.PlayerHooks.OnPlayerChat(tsplr, args.Text, ref text);
Utils.Broadcast(text, tsplr.Group.R, tsplr.Group.G, tsplr.Group.B);
//Invoke the PlayerChat hook. If this hook event is handled then we need to prevent sending the chat message
bool cancelChat = PlayerHooks.OnPlayerChat(tsplr, args.Text, ref text);
args.Handled = true;
if (cancelChat)
{
return;
}
Utils.Broadcast(text, tsplr.Group.R, tsplr.Group.G, tsplr.Group.B);
}
else
{
@ -1337,7 +1370,13 @@ namespace TShockAPI
//Give that poor player their name back :'c
ply.name = name;
PlayerHooks.OnPlayerChat(tsplr, args.Text, ref text);
bool cancelChat = PlayerHooks.OnPlayerChat(tsplr, args.Text, ref text);
if (cancelChat)
{
args.Handled = true;
return;
}
//This netpacket is used to send chat text from the server to clients, in this case on behalf of a client
Terraria.Net.NetPacket packet = Terraria.GameContent.NetModules.NetTextModule.SerializeServerMessage(

View file

@ -70,7 +70,7 @@
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="OTAPI=">
<Reference Include="OTAPI">
<SpecificVersion>False</SpecificVersion>
<HintPath Condition="Exists('..\TerrariaServerAPI\TerrariaServerAPI\bin\$(ConfigurationName)\OTAPI.dll')">..\TerrariaServerAPI\TerrariaServerAPI\bin\$(ConfigurationName)\OTAPI.dll</HintPath>
</Reference>

@ -1 +1 @@
Subproject commit 2a0b338a3c4fb82e05946082ab31def771b783fb
Subproject commit 96c1bc95fe7526b294f65dc711a1494b5869c9ea