Merge branch 'general-devel' into pscheck

This commit is contained in:
Chris 2021-04-13 16:32:14 +09:30 committed by GitHub
commit 3664b81c3f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 531 additions and 127 deletions

View file

@ -13,12 +13,28 @@ 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!
* 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)
## 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

@ -585,6 +585,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."
@ -2421,6 +2426,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 +2440,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 +2536,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 +5303,15 @@ namespace TShockAPI
args.Player.SendFileTextAsMessage(FileTools.RulesPath);
}
private static void Whisper(CommandArgs args)
public static bool[] WDisabled { get; set; } = new bool[256];
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)
{
@ -5308,6 +5329,11 @@ namespace TShockAPI
{
var plr = players[0];
var msg = string.Join(" ", args.Parameters.ToArray(), 1, args.Parameters.Count - 1);
if (WDisabled[players[0].Index])
{
args.Player.SendErrorMessage("This player has disabled people from sending whispers!");
return;
}
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);
plr.LastWhisper = args.Player;
@ -5315,6 +5341,19 @@ namespace TShockAPI
}
}
public static void Wallow(CommandArgs args)
{
int index = args.Player.Index;
if (WDisabled[index])
{
args.Player.SendSuccessMessage("You will now recieve whispers from other players!");
WDisabled[index] = !WDisabled[index];
return;
}
WDisabled[index] = !WDisabled[index];
args.Player.SendSuccessMessage("You will now not recieve whispers from other players, type '/wallow' to recieve them again!");
}
private static void Reply(CommandArgs args)
{
if (args.Player.mute)
@ -5410,7 +5449,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 +6049,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 +6064,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 +6102,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,12 +6382,28 @@ 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);
args.Player.SendSuccessMessage("Tried to grow a " + name + ".");
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)

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");
}
}
@ -232,7 +258,7 @@ namespace TShockAPI.DB
args.Message = args.Valid ? null : "a current ban for this identifier already exists.";
}
}
/// <summary>
/// Adds a new ban for the given identifier. Returns a Ban object if the ban was added, else null
/// </summary>
@ -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

@ -2704,6 +2704,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;

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.0.1")]
[assembly: AssemblyFileVersion("4.5.0.1")]

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

@ -1557,8 +1557,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 +1637,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

@ -57,7 +57,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 = "Stealownz + DeathCradle 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 +890,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
@ -1323,9 +1330,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 +1352,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 e0302513009220a8d92ba12c867a401e7731d28a