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. * 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 ## 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) ## 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) * 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) * Disabled debug by default. (@hakusaro)
* Changed "WinVer" field in `/serverinfo` to "Operating System". (@Terrabade) * 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) ## TShock 4.4.0 (Pre-release 15)
* Overhauled Bans system. Bans are now based on 'identifiers'. (@QuiCM) * 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." 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") 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." 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; return;
} }
string message = "{0} spawned {1} {2} time(s)";
string spawnName;
NPC npc = new NPC(); NPC npc = new NPC();
switch (args.Parameters[0].ToLower()) switch (args.Parameters[0].ToLower())
{ {
@ -2433,87 +2440,89 @@ namespace TShockAPI
npc.SetDefaults(i); npc.SetDefaults(i);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "all bosses";
return; break;
case "brain": case "brain":
case "brain of cthulhu": case "brain of cthulhu":
case "boc": case "boc":
npc.SetDefaults(266); npc.SetDefaults(266);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "the Brain of Cthulhu";
return; break;
case "destroyer": case "destroyer":
npc.SetDefaults(134); npc.SetDefaults(134);
TSPlayer.Server.SetTime(false, 0.0); TSPlayer.Server.SetTime(false, 0.0);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "the Destroyer";
return; break;
case "duke": case "duke":
case "duke fishron": case "duke fishron":
case "fishron": case "fishron":
npc.SetDefaults(370); npc.SetDefaults(370);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "Duke Fishron";
return; break;
case "eater": case "eater":
case "eater of worlds": case "eater of worlds":
case "eow": case "eow":
npc.SetDefaults(13); npc.SetDefaults(13);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "the Eater of Worlds";
return; break;
case "eye": case "eye":
case "eye of cthulhu": case "eye of cthulhu":
case "eoc": case "eoc":
npc.SetDefaults(4); npc.SetDefaults(4);
TSPlayer.Server.SetTime(false, 0.0); TSPlayer.Server.SetTime(false, 0.0);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "the Eye of Cthulhu";
return; break;
case "golem": case "golem":
npc.SetDefaults(245); npc.SetDefaults(245);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "the Golem";
return; break;
case "king": case "king":
case "king slime": case "king slime":
case "ks": case "ks":
npc.SetDefaults(50); npc.SetDefaults(50);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "the King Slime";
return; break;
case "plantera": case "plantera":
npc.SetDefaults(262); npc.SetDefaults(262);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "Plantera";
return; break;
case "prime": case "prime":
case "skeletron prime": case "skeletron prime":
npc.SetDefaults(127); npc.SetDefaults(127);
TSPlayer.Server.SetTime(false, 0.0); TSPlayer.Server.SetTime(false, 0.0);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "Skeletron Prime";
return; break;
case "queen bee": case "queen bee":
case "qb": case "qb":
npc.SetDefaults(222); npc.SetDefaults(222);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "the Queen Bee";
return; break;
case "skeletron": case "skeletron":
npc.SetDefaults(35); npc.SetDefaults(35);
TSPlayer.Server.SetTime(false, 0.0); TSPlayer.Server.SetTime(false, 0.0);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "Skeletron";
return; break;
case "twins": case "twins":
TSPlayer.Server.SetTime(false, 0.0); TSPlayer.Server.SetTime(false, 0.0);
npc.SetDefaults(125); npc.SetDefaults(125);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
npc.SetDefaults(126); npc.SetDefaults(126);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "the Twins";
return; break;
case "wof": case "wof":
case "wall of flesh": case "wall of flesh":
if (Main.wofNPCIndex != -1) if (Main.wofNPCIndex != -1)
@ -2527,103 +2536,114 @@ namespace TShockAPI
return; return;
} }
NPC.SpawnWOF(new Vector2(args.Player.X, args.Player.Y)); NPC.SpawnWOF(new Vector2(args.Player.X, args.Player.Y));
TSPlayer.All.SendSuccessMessage("{0} has spawned the Wall of Flesh.", args.Player.Name); spawnName = "the Wall of Flesh";
return; break;
case "moon": case "moon":
case "moon lord": case "moon lord":
case "ml": case "ml":
npc.SetDefaults(398); npc.SetDefaults(398);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "the Moon Lord";
return; break;
case "empress": case "empress":
case "empress of light": case "empress of light":
case "eol": case "eol":
npc.SetDefaults(636); npc.SetDefaults(636);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "the Empress of Light";
return; break;
case "queen slime": case "queen slime":
case "qs": case "qs":
npc.SetDefaults(657); npc.SetDefaults(657);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "the Queen Slime";
return; break;
case "lunatic": case "lunatic":
case "lunatic cultist": case "lunatic cultist":
case "cultist": case "cultist":
case "lc": case "lc":
npc.SetDefaults(439); npc.SetDefaults(439);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "the Lunatic Cultist";
return; break;
case "betsy": case "betsy":
npc.SetDefaults(551); npc.SetDefaults(551);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "Betsy";
return; break;
case "flying dutchman": case "flying dutchman":
case "flying": case "flying":
case "dutchman": case "dutchman":
npc.SetDefaults(491); npc.SetDefaults(491);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "the Flying Dutchman";
return; break;
case "mourning wood": case "mourning wood":
npc.SetDefaults(325); npc.SetDefaults(325);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "Mourning Wood";
return; break;
case "pumpking": case "pumpking":
npc.SetDefaults(327); npc.SetDefaults(327);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "the Pumpking";
return; break;
case "everscream": case "everscream":
npc.SetDefaults(344); npc.SetDefaults(344);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "Everscream";
return; break;
case "santa-nk1": case "santa-nk1":
case "santa": case "santa":
npc.SetDefaults(346); npc.SetDefaults(346);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "Santa-NK1";
return; break;
case "ice queen": case "ice queen":
npc.SetDefaults(345); npc.SetDefaults(345);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "the Ice Queen";
return; break;
case "martian saucer": case "martian saucer":
npc.SetDefaults(392); npc.SetDefaults(392);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "a Martian Saucer";
return; break;
case "solar pillar": case "solar pillar":
npc.SetDefaults(517); npc.SetDefaults(517);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "a Solar Pillar";
return; break;
case "nebula pillar": case "nebula pillar":
npc.SetDefaults(507); npc.SetDefaults(507);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "a Nebula Pillar";
return; break;
case "vortex pillar": case "vortex pillar":
npc.SetDefaults(422); npc.SetDefaults(422);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "a Vortex Pillar";
return; break;
case "stardust pillar": case "stardust pillar":
npc.SetDefaults(493); npc.SetDefaults(493);
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY); 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); spawnName = "a Stardust Pillar";
return; break;
default: default:
args.Player.SendErrorMessage("Invalid boss type!"); args.Player.SendErrorMessage("Invalid boss type!");
return; 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) private static void SpawnMob(CommandArgs args)
@ -5283,14 +5303,15 @@ namespace TShockAPI
args.Player.SendFileTextAsMessage(FileTools.RulesPath); 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) 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; return;
} }
var players = TSPlayer.FindByNameOrID(args.Parameters[0]); var players = TSPlayer.FindByNameOrID(args.Parameters[0]);
if (players.Count == 0) if (players.Count == 0)
{ {
@ -5308,6 +5329,11 @@ namespace TShockAPI
{ {
var plr = players[0]; var plr = players[0];
var msg = string.Join(" ", args.Parameters.ToArray(), 1, args.Parameters.Count - 1); 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); 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); args.Player.SendMessage(String.Format("<To {0}> {1}", plr.Name, msg), Color.MediumPurple);
plr.LastWhisper = args.Player; 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) private static void Reply(CommandArgs args)
{ {
if (args.Player.mute) if (args.Player.mute)
@ -5410,7 +5449,7 @@ namespace TShockAPI
type = 170; type = 170;
} }
var ply = players[0]; 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(); Main.projectile[p].Kill();
args.Player.SendSuccessMessage("Launched Firework on {0}.", ply.Name); 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) bool growevilAmb = args.Player.HasPermission(Permissions.growevil);
{ string subcmd = args.Parameters.Count == 0 ? "help" : args.Parameters[0].ToLower();
args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}grow <tree/epictree/mushroom/cactus/herb>", Specifier);
return;
}
var name = "Fail"; var name = "Fail";
var x = args.Player.TileX; var x = args.Player.TileX;
var y = args.Player.TileY + 3; var y = args.Player.TileY + 3;
@ -6027,10 +6064,37 @@ namespace TShockAPI
return; return;
} }
switch (args.Parameters[0].ToLower()) switch (subcmd)
{ {
case "tree": case "help":
for (int i = x - 1; i < x + 2; i++) {
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].active(true);
Main.tile[i, y].type = 2; Main.tile[i, y].type = 2;
@ -6038,37 +6102,279 @@ namespace TShockAPI
} }
Main.tile[x, y - 1].wall = 0; Main.tile[x, y - 1].wall = 0;
WorldGen.GrowTree(x, y); WorldGen.GrowTree(x, y);
name = "Tree"; name = "Basic Tree";
break; 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].active(true);
Main.tile[i, y].type = 2; Main.tile[i, y].type = 2;
Main.tile[i, y].wall = 0; Main.tile[i, y].wall = 0;
} }
Main.tile[x, y - 1].wall = 0; Main.tile[x, y - 1].wall = 0;
Main.tile[x, y - 1].liquid = 0; WorldGen.TryGrowingTreeByType(596, x, y);
Main.tile[x, y - 1].active(true); name = "Sakura Tree";
WorldGen.GrowEpicTree(x, y);
name = "Epic Tree";
break; 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].active(true);
Main.tile[i, y].type = 70; Main.tile[i, y].type = 2;
Main.tile[i, y].wall = 0; Main.tile[i, y].wall = 0;
} }
Main.tile[x, y - 1].wall = 0; Main.tile[x, y - 1].wall = 0;
WorldGen.GrowShroom(x, y); WorldGen.TryGrowingTreeByType(616, x, y);
name = "Mushroom"; name = "Willow Tree";
break; 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": case "cactus":
Main.tile[x, y].type = 53; Main.tile[x, y].type = 53;
WorldGen.GrowCactus(x, y); WorldGen.GrowCactus(x, y);
name = "Cactus"; name = "Cactus";
break; break;
case "herb": case "herb":
Main.tile[x, y].active(true); Main.tile[x, y].active(true);
Main.tile[x, y].frameX = 36; Main.tile[x, y].frameX = 36;
@ -6076,12 +6382,28 @@ namespace TShockAPI
WorldGen.GrowAlch(x, y); WorldGen.GrowAlch(x, y);
name = "Herb"; name = "Herb";
break; 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: default:
args.Player.SendErrorMessage("Unknown plant!"); args.Player.SendErrorMessage("Unknown plant!");
return; return;
} }
args.Player.SendTileSquare(x, y); if (args.Parameters.Count == 1)
args.Player.SendSuccessMessage("Tried to grow a " + name + "."); {
args.Player.SendTileSquare(x - 2, y - 20, 25);
args.Player.SendSuccessMessage("Tried to grow a " + name + ".");
}
} }
private static void ToggleGodMode(CommandArgs args) private static void ToggleGodMode(CommandArgs args)

View file

@ -37,18 +37,7 @@ namespace TShockAPI.DB
/// <summary> /// <summary>
/// Readonly dictionary of Bans, keyed on ban ticket number. /// Readonly dictionary of Bans, keyed on ban ticket number.
/// </summary> /// </summary>
public ReadOnlyDictionary<int, Ban> Bans public ReadOnlyDictionary<int, Ban> Bans => new ReadOnlyDictionary<int, Ban>(_bans);
{
get
{
if (_bans == null)
{
_bans = RetrieveAllBans().ToDictionary(b => b.TicketNumber);
}
return new ReadOnlyDictionary<int, Ban>(_bans);
}
}
/// <summary> /// <summary>
/// Event invoked when a ban is checked for validity /// 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)"); throw new Exception("Could not find a database library (probably Sqlite3.dll)");
} }
EnsureBansCollection();
TryConvertBans(); TryConvertBans();
OnBanValidate += BanValidateCheck; OnBanValidate += BanValidateCheck;
OnBanPreAdd += BanAddedCheck; 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> /// <summary>
/// Converts bans from the old ban system to the new. /// Converts bans from the old ban system to the new.
/// </summary> /// </summary>
@ -107,7 +108,7 @@ namespace TShockAPI.DB
int res; int res;
if (database.GetSqlType() == SqlType.Mysql) 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 else
{ {
@ -116,6 +117,7 @@ namespace TShockAPI.DB
if (res != 0) if (res != 0)
{ {
var bans = new List<BanPreAddEventArgs>();
using (var reader = database.QueryReader("SELECT * FROM Bans")) using (var reader = database.QueryReader("SELECT * FROM Bans"))
{ {
while (reader.Read()) while (reader.Read())
@ -140,22 +142,46 @@ namespace TShockAPI.DB
if (!string.IsNullOrWhiteSpace(ip)) 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)) 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)) 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");
} }
} }
@ -252,7 +278,16 @@ namespace TShockAPI.DB
BanDateTime = fromDate, BanDateTime = fromDate,
ExpirationDateTime = toDate 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); OnBanPreAdd?.Invoke(this, args);
if (!args.Valid) if (!args.Valid)
@ -265,21 +300,21 @@ namespace TShockAPI.DB
if (database.GetSqlType() == SqlType.Mysql) if (database.GetSqlType() == SqlType.Mysql)
{ {
query += "SELECT CAST(LAST_INSERT_ID() as INT);"; query += "SELECT LAST_INSERT_ID();";
} }
else else
{ {
query += "SELECT CAST(last_insert_rowid() as INT);"; 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) if (ticketId == 0)
{ {
return new AddBanResult { Message = "Database insert failed." }; 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); _bans.Add(ticketId, b);
OnBanPostAdd?.Invoke(this, new BanEventArgs { Ban = b }); OnBanPostAdd?.Invoke(this, new BanEventArgs { Ban = b });

View file

@ -2704,6 +2704,7 @@ namespace TShockAPI
float[] ai = new float[Projectile.maxAI]; float[] ai = new float[Projectile.maxAI];
for (int i = 0; i < Projectile.maxAI; ++i) for (int i = 0; i < Projectile.maxAI; ++i)
ai[i] = !bits.AI[i] ? 0.0f : args.Data.ReadSingle(); 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; short dmg = bits.HasDamage ? args.Data.ReadInt16() : (short)0;
float knockback = bits.HasKnockback ? args.Data.ReadSingle() : 0.0f; float knockback = bits.HasKnockback ? args.Data.ReadSingle() : 0.0f;
short origDmg = bits.HasOriginalDamage ? args.Data.ReadInt16() : (short)0; 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="ply">The player firing the event.</param>
/// <param name="rawtext">The raw chat text sent by the player.</param> /// <param name="rawtext">The raw chat text sent by the player.</param>
/// <param name="tshockText">The chat text after being formatted.</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) if (PlayerChat == null)
return; return false;
var args = new PlayerChatEventArgs {Player = ply, RawText = rawtext, TShockFormattedText = tshockText}; var args = new PlayerChatEventArgs {Player = ply, RawText = rawtext, TShockFormattedText = tshockText};
PlayerChat(args); PlayerChat(args);
tshockText = args.TShockFormattedText; tshockText = args.TShockFormattedText;
return args.Handled;
} }
/// <summary> /// <summary>

View file

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

View file

@ -319,6 +319,9 @@ namespace TShockAPI
[Description("User can grow plants.")] [Description("User can grow plants.")]
public static readonly string grow = "tshock.world.grow"; 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.")] [Description("User can change hardmode state.")]
public static readonly string hardmode = "tshock.world.hardmode"; 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 // 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) // so that the update manager works correctly (via the Github releases api and mimic)
[assembly: AssemblyVersion("4.4.0")] [assembly: AssemblyVersion("4.5.0.1")]
[assembly: AssemblyFileVersion("4.4.0")] [assembly: AssemblyFileVersion("4.5.0.1")]

View file

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

View file

@ -1557,8 +1557,6 @@ namespace TShockAPI
public virtual void Disable(string reason = "", DisableFlags flags = DisableFlags.WriteToLog) public virtual void Disable(string reason = "", DisableFlags flags = DisableFlags.WriteToLog)
{ {
LastThreat = DateTime.UtcNow; LastThreat = DateTime.UtcNow;
SetBuff(BuffID.Frozen, 330, true);
SetBuff(BuffID.Stoned, 330, true);
SetBuff(BuffID.Webbed, 330, true); SetBuff(BuffID.Webbed, 330, true);
if (ActiveChest != -1) if (ActiveChest != -1)
@ -1639,7 +1637,7 @@ namespace TShockAPI
if (force) if (force)
{ {
TShock.Bans.InsertBan($"{Identifier.IP}{IP}", reason, adminUserName, DateTime.UtcNow, DateTime.MaxValue); 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) if (Account != null)
{ {
TShock.Bans.InsertBan($"{Identifier.Account}{Account.Name}", reason, adminUserName, DateTime.UtcNow, DateTime.MaxValue); 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> /// <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; 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> /// <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> /// <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"; public static string SavePath = "tshock";
@ -890,6 +890,13 @@ namespace TShockAPI
/// <param name="args">args - EventArgs args</param> /// <param name="args">args - EventArgs args</param>
private void OnUpdate(EventArgs args) 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) if (Backups.IsBackupTime)
Backups.Backup(); Backups.Backup();
//call these every second, not every update //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, text = String.Format(Config.Settings.ChatFormat, tsplr.Group.Name, tsplr.Group.Prefix, tsplr.Name, tsplr.Group.Suffix,
args.Text); 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; args.Handled = true;
if (cancelChat)
{
return;
}
Utils.Broadcast(text, tsplr.Group.R, tsplr.Group.G, tsplr.Group.B);
} }
else else
{ {
@ -1337,7 +1352,13 @@ namespace TShockAPI
//Give that poor player their name back :'c //Give that poor player their name back :'c
ply.name = name; 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 //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( 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"> <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> <HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference> </Reference>
<Reference Include="OTAPI="> <Reference Include="OTAPI">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath Condition="Exists('..\TerrariaServerAPI\TerrariaServerAPI\bin\$(ConfigurationName)\OTAPI.dll')">..\TerrariaServerAPI\TerrariaServerAPI\bin\$(ConfigurationName)\OTAPI.dll</HintPath> <HintPath Condition="Exists('..\TerrariaServerAPI\TerrariaServerAPI\bin\$(ConfigurationName)\OTAPI.dll')">..\TerrariaServerAPI\TerrariaServerAPI\bin\$(ConfigurationName)\OTAPI.dll</HintPath>
</Reference> </Reference>

@ -1 +1 @@
Subproject commit 2a0b338a3c4fb82e05946082ab31def771b783fb Subproject commit e0302513009220a8d92ba12c867a401e7731d28a