Merge branch 'general-devel' into general-devel
This commit is contained in:
commit
38d99de060
16 changed files with 853 additions and 88 deletions
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
|
|
@ -1,3 +1,2 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
custom: https://www.givedirectly.org/
|
||||
|
|
|
|||
8
.github/ISSUE_TEMPLATE/defect-report.md
vendored
8
.github/ISSUE_TEMPLATE/defect-report.md
vendored
|
|
@ -9,6 +9,8 @@ assignees: ''
|
|||
|
||||
<!-- Please provide the information requested below -->
|
||||
|
||||
<!-- STOP! Please set DebugLogs to true in your config file, restart the server, and then produce the problem again. Please attach the new debug log to this report. -->
|
||||
|
||||
* TShock version:
|
||||
* TShock build number (if known):
|
||||
|
||||
|
|
@ -31,8 +33,12 @@ PUT SUPER LONG ERROR MESSAGES IN THE TICK MARKS
|
|||
|
||||
<!-- If providing screenshots of client, send before and after pressing F8 to show network debug -->
|
||||
|
||||
#### Any log messages from files that end in `.log` or `.txt`?
|
||||
#### Any log messages from files that end in `.log` or `.txt`? What are the last 100 log messages from the server console?
|
||||
|
||||
<!-- Please add context. Even if you don't have an error message, please show recent server logs at least. -->
|
||||
|
||||
<!-- STOP! If you do not provide any logs or screenshots, your issue may be closed or converted to a discussion without warning! -->
|
||||
|
||||
#### What plugins and what versions of those plugins are you running?
|
||||
|
||||
If I didn't provide any logs this issue, please close my issue immediately. I'm sorry for the inconvenience.
|
||||
|
|
|
|||
52
CHANGELOG.md
52
CHANGELOG.md
|
|
@ -2,10 +2,58 @@
|
|||
|
||||
This is the rolling changelog for TShock for Terraria. Use past tense when adding new entries; sign your name off when you add or change something. This should primarily be things like user changes, not necessarily codebase changes unless it's really relevant or large.
|
||||
|
||||
## Upcoming release
|
||||
## Upcoming Release
|
||||
* Fixed pet licenses. (@Olink)
|
||||
* Added initial support for Journey mode in SSC worlds. (@Olink)
|
||||
* Made TShock database MySQL 8 compatible by escaping column names in our IQueryBuilder code. (Name `Groups` is a reserved element in this version, which is used in our `Region` table.) (@Patrikkk)
|
||||
* Reintroduced `-worldselectpath` per feedback from @fjfnaranjo. This command line argument should be used to specify the place where the interactive server startup will look for worlds to show on the world select screen. The original version of this argument, `-worldpath`, was removed because several game service providers have broken configurations that stop the server from running with an unhelpful error. This specific configuration was `-world` and `-worldpath`. In the new world, you can do the following:
|
||||
* `-worldselectpath` should be used if you want to customize the server interactive boot world list (so that you can select from a number of worlds in non-standard locations).
|
||||
* `-world` will behave as an absolute path to the world to load. This is the most common thing you want if you're starting the server and have a specific world in mind.
|
||||
* `-worldselectpath` and `-worldname` should work together enabling you to select from a world from the list that you specify. This is *not* a world file name, but a world name as described by Terraria.
|
||||
* `-worldselectpath` is identical to the old `-worldpath`. If you specify `-worldselectpath` and `-world` without specifying an absolute path the server will crash for sure.
|
||||
* Thank you again to @fjfnaranjo for supplying a [detailed feature request](https://github.com/Pryaxis/TShock/issues/1914) explaining precisely why this option should be available. Without this, we would have had no context as to why this feature was useful or important. Thank you, @fjfnaranjo!
|
||||
* This change was implemented by (@QuiCM, @hakusaro).
|
||||
* Updated Bouncer to include Sparkle Slime debuff that can be applied to town NPCs. (@moisterrific)
|
||||
* Updated `/spawnboss` command to include Empress of Light and Queen Slime. (@moisterrific)
|
||||
* Added journey mode permissions to owner group by default. (@moisterrific)
|
||||
* Fixed kick on hardcore death / kick on mediumcore death / ban on either from taking action against journey mode players. (@hakusaro)
|
||||
* Attempted to fix the problem with the magic mirror spawn problems. You should be able to remove your spawn point in SSC by right clicking on a bed now. (@hakusaro, @AxeelAnder)
|
||||
* Added HandleFoodPlatterTryPlacing event, which is called whenever a player places a food in a plate. Add antihack to bouncer, to prevent removing food from plates if the region is protected; To prevent placement if they are not in range; To prevent placement if the item is not placed from player hand. (@Patrikkk)
|
||||
* Fixed an offset error in NetTile that impacted `SendTileSquare`. It was being read as a `byte` and not a `ushort`. (@QuiCM)
|
||||
* Fixed coins not dropping after being picked up by npcs. The ExtraValue packet was not being read correctly. (@Olink)
|
||||
* Removed packet monitoring from debug logs. To achieve the same results, install @QuiCM's packet monitor plugin (it does better things). (@hakusaro)
|
||||
* Updated packet monitoring in send tile square handler for Bouncer debugging. (@hakusaro)
|
||||
* Added `/sync`, activated with `tshock.synclocalarea`. This is a default guest permission. When the command is issued, the server will resync area around the player in the event of a desync issue. (@hakusaro)
|
||||
* If your doors disappear, this command will allow a player to resync without having to disconnect from the server.
|
||||
* The default group that gets this permission is `Guest` for the time being.
|
||||
* To add this command to your guest group, give them `tshock.synclocalarea`, with `/group addperm guest tshock.synclocalarea`.
|
||||
* This command may be removed at any time in the future (and will likely be removed when send tile square handling is fixed).
|
||||
|
||||
## TShock 4.4.0 (Pre-release 8)
|
||||
* Update for OTAPI 2.0.0.36 and Terraria 1.4.0.4. (@hakusaro, @Patrikkk, @DeathCradle)
|
||||
* Fixed /wind command. (@AxeelAnder)
|
||||
* Fixed NPC buff bouncer. (@AxeelAnder)
|
||||
* Fixed NPC debuff issue when attempting to fight bosses resulting in kicks. (@AxeelAnder)
|
||||
* Fixed players are unable to remove an NPC. Change `byte NPCHomeChangeEventArgs.Homeless` to `HouseholdStatus NPCHomeChangeEventArgs.HouseholdStatus`. (@AxeelAnder)
|
||||
* Fixed lava, wet, honey, and dry bombs;
|
||||
and lava, wet, honey, and dry grenades;
|
||||
and lava, wet, honey, and dry rockets;
|
||||
and lava, wet, honey, and dry mines. (@Olink)
|
||||
* Fix Bloody Tear displaying the wrong text when used. (@Olink)
|
||||
* Fix the visibility toggle for the last two accessory slots. (@Olink)
|
||||
* Adding Journey mode user account permissions. Journey mode must be enabled for these to have any effect. (@Patrikkk)
|
||||
* `tshock.journey.time.freeze`
|
||||
* `tshock.journey.time.set`
|
||||
* `tshock.journey.time.setspeed`
|
||||
* `tshock.journey.godmode`
|
||||
* `tshock.journey.wind.strength`
|
||||
* `tshock.journey.wind.freeze`
|
||||
* `tshock.journey.rain.strength`
|
||||
* `tshock.journey.rain.freeze`
|
||||
* `tshock.journey.placementrange`
|
||||
* `tshock.journey.setdifficulty`
|
||||
* `tshock.journey.biomespreadfreeze`
|
||||
* `tshock.journey.setspawnrate`
|
||||
* Changed default thresholds for some changes in the config file to accommodate new items & changes to Terraria. (@hakusaro)
|
||||
|
||||
## TShock 4.4.0 (Pre-release 7 (Entangled))
|
||||
* Fixed bed spawn issues when trying to remove spawn point in SSC. (@Olink)
|
||||
|
|
|
|||
|
|
@ -19,18 +19,17 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Terraria.ID;
|
||||
using TShockAPI.DB;
|
||||
using TShockAPI.Net;
|
||||
using Terraria;
|
||||
using Microsoft.Xna.Framework;
|
||||
using OTAPI.Tile;
|
||||
using TShockAPI.Localization;
|
||||
using static TShockAPI.GetDataHandlers;
|
||||
using TerrariaApi.Server;
|
||||
using Terraria.ObjectData;
|
||||
using Terraria.DataStructures;
|
||||
using Terraria.Localization;
|
||||
using TShockAPI.Models.PlayerUpdate;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TShockAPI
|
||||
{
|
||||
|
|
@ -68,6 +67,7 @@ namespace TShockAPI
|
|||
GetDataHandlers.MassWireOperation += OnMassWireOperation;
|
||||
GetDataHandlers.PlayerDamage += OnPlayerDamage;
|
||||
GetDataHandlers.KillMe += OnKillMe;
|
||||
GetDataHandlers.FoodPlatterTryPlacing += OnFoodPlatterTryPlacing;
|
||||
}
|
||||
|
||||
internal void OnGetSection(object sender, GetDataHandlers.GetSectionEventArgs args)
|
||||
|
|
@ -528,7 +528,7 @@ namespace TShockAPI
|
|||
|
||||
if (args.Player.HasPermission(Permissions.allowclientsideworldedit))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected clientside world edit from {0}", args.Player.Name);
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare accepted clientside world edit from {0}", args.Player.Name);
|
||||
args.Handled = false;
|
||||
return;
|
||||
}
|
||||
|
|
@ -558,6 +558,8 @@ namespace TShockAPI
|
|||
return;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
bool failed = false;
|
||||
try
|
||||
{
|
||||
var tiles = new NetTile[size, size];
|
||||
|
|
@ -569,7 +571,6 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
int realx = tileX + x;
|
||||
|
|
@ -709,9 +710,10 @@ namespace TShockAPI
|
|||
catch
|
||||
{
|
||||
args.Player.SendTileSquare(tileX, tileY, size);
|
||||
failed = true;
|
||||
}
|
||||
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected from spaghetti from {0}", args.Player.Name);
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare from {0} {1} {2}", args.Player.Name, changed, failed);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
|
|
@ -865,7 +867,6 @@ namespace TShockAPI
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
if (stabProjectile.ContainsKey(type))
|
||||
{
|
||||
if (stabProjectile[type] == args.Player.TPlayer.HeldItem.type)
|
||||
|
|
@ -875,7 +876,6 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Main.projHostile contains projectiles that can harm players
|
||||
// without PvP enabled and belong to enemy mobs, so they shouldn't be
|
||||
// possible for players to create. (Source: Ijwu, QuiCM)
|
||||
|
|
@ -888,6 +888,8 @@ namespace TShockAPI
|
|||
}
|
||||
|
||||
// Tombstones should never be permitted by players
|
||||
// This check means like, invalid or hacked tombstones (sent from hacked clients)
|
||||
// Death does not create a tombstone projectile by default
|
||||
if (type == ProjectileID.Tombstone)
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / OnNewProjectile rejected from tombstones from {0}", args.Player.Name);
|
||||
|
|
@ -1290,6 +1292,24 @@ namespace TShockAPI
|
|||
args.Player.TileLiquidThreshold++;
|
||||
}
|
||||
|
||||
bool wasThereABombNearby = false;
|
||||
lock (args.Player.RecentlyCreatedProjectiles)
|
||||
{
|
||||
IEnumerable<int> projectileTypesThatPerformThisOperation;
|
||||
if (amount > 0) //handle the projectiles that create fluid.
|
||||
{
|
||||
projectileTypesThatPerformThisOperation = projectileCreatesLiquid.Where(k => k.Value == type).Select(k => k.Key);
|
||||
}
|
||||
else //handle the scenario where we are removing liquid
|
||||
{
|
||||
projectileTypesThatPerformThisOperation = projectileCreatesLiquid.Where(k => k.Value == LiquidType.Removal).Select(k => k.Key);
|
||||
}
|
||||
|
||||
var recentBombs = args.Player.RecentlyCreatedProjectiles.Where(p => projectileTypesThatPerformThisOperation.Contains(Main.projectile[p.Index].type));
|
||||
wasThereABombNearby = recentBombs.Any(r => Math.Abs(args.TileX - (Main.projectile[r.Index].position.X / 16.0f)) < TShock.Config.BombExplosionRadius
|
||||
&& Math.Abs(args.TileY - (Main.projectile[r.Index].position.Y / 16.0f)) < TShock.Config.BombExplosionRadius);
|
||||
}
|
||||
|
||||
// Liquid anti-cheat
|
||||
// Arguably the banned buckets bit should be in the item bans system
|
||||
if (amount != 0)
|
||||
|
|
@ -1326,7 +1346,7 @@ namespace TShockAPI
|
|||
bucket = 6;
|
||||
}
|
||||
|
||||
if (type == LiquidType.Lava && !(bucket == 2 || bucket == 0 || bucket == 5 || bucket == 6))
|
||||
if (!wasThereABombNearby && type == LiquidType.Lava && !(bucket == 2 || bucket == 0 || bucket == 5 || bucket == 6))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected bucket check 1 from {0}", args.Player.Name);
|
||||
args.Player.SendErrorMessage("You do not have permission to perform this action.");
|
||||
|
|
@ -1336,7 +1356,7 @@ namespace TShockAPI
|
|||
return;
|
||||
}
|
||||
|
||||
if (type == LiquidType.Lava && TShock.Itembans.ItemIsBanned("Lava Bucket", args.Player))
|
||||
if (!wasThereABombNearby && type == LiquidType.Lava && TShock.Itembans.ItemIsBanned("Lava Bucket", args.Player))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected lava bucket from {0}", args.Player.Name);
|
||||
args.Player.SendErrorMessage("You do not have permission to perform this action.");
|
||||
|
|
@ -1346,7 +1366,7 @@ namespace TShockAPI
|
|||
return;
|
||||
}
|
||||
|
||||
if (type == LiquidType.Water && !(bucket == 1 || bucket == 0 || bucket == 4))
|
||||
if (!wasThereABombNearby && type == LiquidType.Water && !(bucket == 1 || bucket == 0 || bucket == 4))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected bucket check 2 from {0}", args.Player.Name);
|
||||
args.Player.SendErrorMessage("You do not have permission to perform this action.");
|
||||
|
|
@ -1356,7 +1376,7 @@ namespace TShockAPI
|
|||
return;
|
||||
}
|
||||
|
||||
if (type == LiquidType.Water && TShock.Itembans.ItemIsBanned("Water Bucket", args.Player))
|
||||
if (!wasThereABombNearby && type == LiquidType.Water && TShock.Itembans.ItemIsBanned("Water Bucket", args.Player))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected bucket check 3 from {0}", args.Player.Name);
|
||||
args.Player.SendErrorMessage("You do not have permission to perform this action.");
|
||||
|
|
@ -1366,7 +1386,7 @@ namespace TShockAPI
|
|||
return;
|
||||
}
|
||||
|
||||
if (type == LiquidType.Honey && !(bucket == 3 || bucket == 0))
|
||||
if (!wasThereABombNearby && type == LiquidType.Honey && !(bucket == 3 || bucket == 0))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected bucket check 4 from {0}", args.Player.Name);
|
||||
args.Player.SendErrorMessage("You do not have permission to perform this action.");
|
||||
|
|
@ -1376,7 +1396,7 @@ namespace TShockAPI
|
|||
return;
|
||||
}
|
||||
|
||||
if (type == LiquidType.Honey && TShock.Itembans.ItemIsBanned("Honey Bucket", args.Player))
|
||||
if (!wasThereABombNearby && type == LiquidType.Honey && TShock.Itembans.ItemIsBanned("Honey Bucket", args.Player))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected bucket check 5 from {0}", args.Player.Name);
|
||||
args.Player.SendErrorMessage("You do not have permission to perform this action.");
|
||||
|
|
@ -1395,7 +1415,7 @@ namespace TShockAPI
|
|||
return;
|
||||
}
|
||||
|
||||
if (!args.Player.IsInRange(tileX, tileY, 16))
|
||||
if (!wasThereABombNearby && !args.Player.IsInRange(tileX, tileY, 16))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected range checks from {0}", args.Player.Name);
|
||||
args.Player.SendTileSquare(tileX, tileY, 1);
|
||||
|
|
@ -1520,7 +1540,7 @@ namespace TShockAPI
|
|||
if (npc.townNPC && npc.netID != NPCID.Guide && npc.netID != NPCID.Clothier)
|
||||
{
|
||||
if (type != BuffID.Lovestruck && type != BuffID.Stinky && type != BuffID.DryadsWard &&
|
||||
type != BuffID.Wet && type != BuffID.Slimed)
|
||||
type != BuffID.Wet && type != BuffID.Slimed && type != BuffID.GelBalloonBuff)
|
||||
{
|
||||
detectedNPCBuffTimeCheat = true;
|
||||
}
|
||||
|
|
@ -1547,7 +1567,6 @@ namespace TShockAPI
|
|||
int id = args.ID;
|
||||
short x = args.X;
|
||||
short y = args.Y;
|
||||
byte homeless = args.Homeless;
|
||||
|
||||
if (!args.Player.HasBuildPermission(x, y))
|
||||
{
|
||||
|
|
@ -1558,7 +1577,8 @@ namespace TShockAPI
|
|||
return;
|
||||
}
|
||||
|
||||
if (!args.Player.IsInRange(x, y))
|
||||
// When kicking out an npc, x and y in args are 0, we shouldn't check range at this case
|
||||
if (args.HouseholdStatus != HouseholdStatus.Homeless && !args.Player.IsInRange(x, y))
|
||||
{
|
||||
args.Player.SendData(PacketTypes.UpdateNPCHome, "", id, Main.npc[id].homeTileX, Main.npc[id].homeTileY,
|
||||
Convert.ToByte(Main.npc[id].homeless));
|
||||
|
|
@ -2045,6 +2065,69 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a player is trying to place an item into a food plate.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
internal void OnFoodPlatterTryPlacing(object sender, GetDataHandlers.FoodPlatterTryPlacingEventArgs args)
|
||||
{
|
||||
if ((args.Player.SelectedItem.type != args.ItemID && args.Player.ItemInHand.type != args.ItemID))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / OnFoodPlatterTryPlacing rejected item not placed by hand from {0}", args.Player.Name);
|
||||
args.Player.SendTileSquare(args.TileX, args.TileY, 1);
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
if (args.Player.IsBeingDisabled())
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / OnFoodPlatterTryPlacing rejected disabled from {0}", args.Player.Name);
|
||||
Item item = new Item();
|
||||
item.netDefaults(args.ItemID);
|
||||
args.Player.GiveItemCheck(args.ItemID, item.Name, args.Stack, args.Prefix);
|
||||
args.Player.SendTileSquare(args.TileX, args.TileY, 1);
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.Player.HasBuildPermission(args.TileX, args.TileY))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / OnFoodPlatterTryPlacing rejected permissions from {0}", args.Player.Name);
|
||||
Item item = new Item();
|
||||
item.netDefaults(args.ItemID);
|
||||
args.Player.GiveItemCheck(args.ItemID, item.Name, args.Stack, args.Prefix);
|
||||
args.Player.SendTileSquare(args.TileX, args.TileY, 1);
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.Player.IsInRange(args.TileX, args.TileY, range: 13)) // To my knowledge, max legit tile reach with accessories.
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / OnFoodPlatterTryPlacing rejected range checks from {0}", args.Player.Name);
|
||||
args.Player.SendTileSquare(args.TileX, args.TileY, 1);
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnSecondUpdate()
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
foreach (var player in TShock.Players)
|
||||
{
|
||||
if (player != null && player.TPlayer.whoAmI >= 0)
|
||||
{
|
||||
var threshold = DateTime.Now.AddSeconds(-5);
|
||||
lock (player.RecentlyCreatedProjectiles)
|
||||
{
|
||||
player.RecentlyCreatedProjectiles = player.RecentlyCreatedProjectiles.Where(s => s.CreatedAt > threshold).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// These time values are references from Projectile.cs, at npc.AddBuff() calls.
|
||||
private static Dictionary<int, short> NPCAddBuffTimeMax = new Dictionary<int, short>()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -589,6 +589,10 @@ namespace TShockAPI
|
|||
{
|
||||
HelpText = "Creates a reference tables for Terraria data types and the TShock permission system in the server folder."
|
||||
});
|
||||
add(new Command(Permissions.synclocalarea, SyncLocalArea, "sync")
|
||||
{
|
||||
HelpText = "Sends all tiles from the server to the player to resync the client with the actual world state."
|
||||
});
|
||||
#endregion
|
||||
|
||||
add(new Command(Aliases, "aliases")
|
||||
|
|
@ -2378,7 +2382,7 @@ namespace TShockAPI
|
|||
{
|
||||
case "*":
|
||||
case "all":
|
||||
int[] npcIds = { 4, 13, 35, 50, 125, 126, 127, 134, 222, 245, 262, 266, 370, 398 };
|
||||
int[] npcIds = { 4, 13, 35, 50, 125, 126, 127, 134, 222, 245, 262, 266, 370, 398, 636, 657 };
|
||||
TSPlayer.Server.SetTime(false, 0.0);
|
||||
foreach (int i in npcIds)
|
||||
{
|
||||
|
|
@ -2442,7 +2446,6 @@ namespace TShockAPI
|
|||
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;
|
||||
case "queen":
|
||||
case "queen bee":
|
||||
npc.SetDefaults(222);
|
||||
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
|
||||
|
|
@ -2483,6 +2486,17 @@ namespace TShockAPI
|
|||
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;
|
||||
case "empress":
|
||||
case "empress of light":
|
||||
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;
|
||||
case "queen slime":
|
||||
npc.SetDefaults(657);
|
||||
TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
|
||||
TSPlayer.All.SendSuccessMessage("{0} has spawned the Queen Slime {1} time(s).", args.Player.Name, amount);
|
||||
return;
|
||||
default:
|
||||
args.Player.SendErrorMessage("Invalid boss type!");
|
||||
return;
|
||||
|
|
@ -5302,6 +5316,13 @@ namespace TShockAPI
|
|||
return;
|
||||
}
|
||||
|
||||
private static void SyncLocalArea(CommandArgs args)
|
||||
{
|
||||
args.Player.SendTileSquare((int) args.Player.TileX, (int) args.Player.TileY, 32);
|
||||
args.Player.SendWarningMessage("Sync'd!");
|
||||
return;
|
||||
}
|
||||
|
||||
#endregion General Commands
|
||||
|
||||
#region Cheat Commands
|
||||
|
|
|
|||
|
|
@ -275,11 +275,11 @@ namespace TShockAPI
|
|||
|
||||
/// <summary>Disables a player and reverts their actions if this number of tile places is exceeded within 1 second.</summary>
|
||||
[Description("Disables a player and reverts their actions if this number of tile places is exceeded within 1 second.")]
|
||||
public int TilePlaceThreshold = 20;
|
||||
public int TilePlaceThreshold = 32;
|
||||
|
||||
/// <summary>Disables a player if this number of liquid sets is exceeded within 1 second.</summary>
|
||||
[Description("Disables a player if this number of liquid sets is exceeded within 1 second.")]
|
||||
public int TileLiquidThreshold = 15;
|
||||
public int TileLiquidThreshold = 50;
|
||||
|
||||
/// <summary>Disable a player if this number of projectiles is created within 1 second.</summary>
|
||||
[Description("Disable a player if this number of projectiles is created within 1 second.")]
|
||||
|
|
@ -527,6 +527,9 @@ namespace TShockAPI
|
|||
[Description("Whether or not the server should output debug level messages related to system operation.")]
|
||||
public bool DebugLogs = false;
|
||||
|
||||
[Description("Determines the range in tiles that a bomb can affect tiles from detonation point.")]
|
||||
public int BombExplosionRadius = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Reads a configuration file from a given path
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -64,7 +64,8 @@ namespace TShockAPI.DB
|
|||
Permissions.canlogin,
|
||||
Permissions.canpartychat,
|
||||
Permissions.cantalkinthird,
|
||||
Permissions.canchat));
|
||||
Permissions.canchat,
|
||||
Permissions.synclocalarea));
|
||||
|
||||
AddDefaultGroup("default", "guest",
|
||||
string.Join(",",
|
||||
|
|
@ -172,7 +173,19 @@ namespace TShockAPI.DB
|
|||
Permissions.settempgroup,
|
||||
Permissions.spawnrate,
|
||||
Permissions.tpoverride,
|
||||
Permissions.createdumps));
|
||||
Permissions.createdumps,
|
||||
Permissions.journey_timefreeze,
|
||||
Permissions.journey_timeset,
|
||||
Permissions.journey_timespeed,
|
||||
Permissions.journey_godmode,
|
||||
Permissions.journey_windstrength,
|
||||
Permissions.journey_windfreeze,
|
||||
Permissions.journey_rainstrength,
|
||||
Permissions.journey_rainfreeze,
|
||||
Permissions.journey_placementrange,
|
||||
Permissions.journey_setdifficulty,
|
||||
Permissions.journey_biomespreadfreeze,
|
||||
Permissions.journey_setspawnrate));
|
||||
}
|
||||
|
||||
// Load Permissions from the DB
|
||||
|
|
|
|||
|
|
@ -192,12 +192,12 @@ namespace TShockAPI.DB
|
|||
var columns =
|
||||
table.Columns.Select(
|
||||
c =>
|
||||
"{0} {1} {2} {3} {4} {5}".SFormat(c.Name, DbTypeToString(c.Type, c.Length),
|
||||
"`{0}` {1} {2} {3} {4} {5}".SFormat(c.Name, DbTypeToString(c.Type, c.Length),
|
||||
c.Primary ? "PRIMARY KEY" : "",
|
||||
c.AutoIncrement ? "AUTO_INCREMENT" : "",
|
||||
c.NotNull ? "NOT NULL" : "",
|
||||
c.DefaultCurrentTimestamp ? "DEFAULT CURRENT_TIMESTAMP" : ""));
|
||||
var uniques = table.Columns.Where(c => c.Unique).Select(c => c.Name);
|
||||
var uniques = table.Columns.Where(c => c.Unique).Select(c => $"`{c.Name}`");
|
||||
return "CREATE TABLE {0} ({1} {2})".SFormat(EscapeTableName(table.Name), string.Join(", ", columns),
|
||||
uniques.Count() > 0
|
||||
? ", UNIQUE({0})".SFormat(string.Join(", ", uniques))
|
||||
|
|
@ -299,7 +299,7 @@ namespace TShockAPI.DB
|
|||
var create = CreateTable(to);
|
||||
// combine all columns in the 'from' variable excluding ones that aren't in the 'to' variable.
|
||||
// exclude the ones that aren't in 'to' variable because if the column is deleted, why try to import the data?
|
||||
var columns = string.Join(", ", from.Columns.Where(c => to.Columns.Any(c2 => c2.Name == c.Name)).Select(c => c.Name));
|
||||
var columns = string.Join(", ", from.Columns.Where(c => to.Columns.Any(c2 => c2.Name == c.Name)).Select(c => $"`{c.Name}`"));
|
||||
var insert = "INSERT INTO {0} ({1}) SELECT {1} FROM {2}".SFormat(escapedTable, columns, tmpTable);
|
||||
var drop = "DROP TABLE {0}".SFormat(tmpTable);
|
||||
return "{0}; {1}; {2}; {3};".SFormat(alter, create, insert, drop);
|
||||
|
|
@ -396,7 +396,7 @@ namespace TShockAPI.DB
|
|||
if (0 == wheres.Count)
|
||||
return string.Empty;
|
||||
|
||||
return "WHERE {0}".SFormat(string.Join(", ", wheres.Select(v => v.Name + " = " + v.Value)));
|
||||
return "WHERE {0}".SFormat(string.Join(", ", wheres.Select(v => $"{v.Name}" + " = " + v.Value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
138
TShockAPI/DB/ResearchDatastore.cs
Normal file
138
TShockAPI/DB/ResearchDatastore.cs
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
using MySql.Data.MySqlClient;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Terraria;
|
||||
using Terraria.ID;
|
||||
|
||||
namespace TShockAPI.DB
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is used as the data interface for Journey mode research.
|
||||
/// This information is maintained such that SSC characters will be properly set up with
|
||||
/// the world's current research.
|
||||
/// </summary>
|
||||
public class ResearchDatastore
|
||||
{
|
||||
private IDbConnection database;
|
||||
|
||||
/// <summary>
|
||||
/// In-memory cache of what items have been sacrificed.
|
||||
/// The first call to GetSacrificedItems will load this with data from the database.
|
||||
/// </summary>
|
||||
private Dictionary<int, int> _itemsSacrificed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TShockAPI.DB.ResearchDatastore"/> class.
|
||||
/// </summary>
|
||||
/// <param name="db">A valid connection to the TShock database</param>
|
||||
public ResearchDatastore(IDbConnection db)
|
||||
{
|
||||
database = db;
|
||||
|
||||
var table = new SqlTable("Research",
|
||||
new SqlColumn("WorldId", MySqlDbType.Int32),
|
||||
new SqlColumn("PlayerId", MySqlDbType.Int32),
|
||||
new SqlColumn("ItemId", MySqlDbType.Int32),
|
||||
new SqlColumn("AmountSacrificed", MySqlDbType.Int32),
|
||||
new SqlColumn("TimeSacrificed", MySqlDbType.DateTime)
|
||||
);
|
||||
var creator = new SqlTableCreator(db,
|
||||
db.GetSqlType() == SqlType.Sqlite
|
||||
? (IQueryBuilder)new SqliteQueryCreator()
|
||||
: new MysqlQueryCreator());
|
||||
try
|
||||
{
|
||||
creator.EnsureTableStructure(table);
|
||||
}
|
||||
catch (DllNotFoundException)
|
||||
{
|
||||
Console.WriteLine("Possible problem with your database - is Sqlite3.dll present?");
|
||||
throw new Exception("Could not find a database library (probably Sqlite3.dll)");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This call will return the memory-cached list of items sacrificed.
|
||||
/// If the cache is not initialized, it will be initialized from the database.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Dictionary<int, int> GetSacrificedItems()
|
||||
{
|
||||
if (_itemsSacrificed == null)
|
||||
{
|
||||
_itemsSacrificed = ReadFromDatabase();
|
||||
}
|
||||
|
||||
return _itemsSacrificed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function will return a Dictionary<ItemId, AmountSacrificed> representing
|
||||
/// what the progress of research on items is for this world.
|
||||
/// </summary>
|
||||
/// <returns>A dictionary of ItemID keys and Amount Sacrificed values.</returns>
|
||||
private Dictionary<int, int> ReadFromDatabase()
|
||||
{
|
||||
Dictionary<int, int> sacrificedItems = new Dictionary<int, int>();
|
||||
|
||||
var sql = @"select itemId, sum(AmountSacrificed) totalSacrificed
|
||||
from Research
|
||||
where WorldId = @0
|
||||
group by itemId";
|
||||
|
||||
try {
|
||||
using (var reader = database.QueryReader(sql, Main.worldID))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
var itemId = reader.Get<Int32>("itemId");
|
||||
var amount = reader.Get<Int32>("totalSacrificed");
|
||||
sacrificedItems[itemId] = amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TShock.Log.Error(ex.ToString());
|
||||
}
|
||||
return sacrificedItems;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method will sacrifice an amount of an item for research.
|
||||
/// </summary>
|
||||
/// <param name="itemId">The net ItemId that is being researched.</param>
|
||||
/// <param name="amount">The amount of items being sacrificed.</param>
|
||||
/// <param name="player">The player who sacrificed the item for research.</param>
|
||||
/// <returns>The cumulative total sacrifices for this item.</returns>
|
||||
public int SacrificeItem(int itemId, int amount, TSPlayer player)
|
||||
{
|
||||
var itemsSacrificed = GetSacrificedItems();
|
||||
if (!(itemsSacrificed.ContainsKey(itemId)))
|
||||
itemsSacrificed[itemId] = 0;
|
||||
|
||||
var sql = @"insert into Research (WorldId, PlayerId, ItemId, AmountSacrificed, TimeSacrificed) values (@0, @1, @2, @3, @4)";
|
||||
|
||||
var result = 0;
|
||||
try
|
||||
{
|
||||
result = database.Query(sql, Main.worldID, player.Account.ID, itemId, amount, DateTime.Now);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TShock.Log.Error(ex.ToString());
|
||||
}
|
||||
|
||||
if (result == 1)
|
||||
{
|
||||
itemsSacrificed[itemId] += amount;
|
||||
}
|
||||
|
||||
return itemsSacrificed[itemId];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -39,6 +39,8 @@ using TShockAPI.Localization;
|
|||
using TShockAPI.Models;
|
||||
using TShockAPI.Models.PlayerUpdate;
|
||||
using TShockAPI.Models.Projectiles;
|
||||
using Terraria.Net;
|
||||
using Terraria.GameContent.NetModules;
|
||||
|
||||
namespace TShockAPI
|
||||
{
|
||||
|
|
@ -125,7 +127,7 @@ namespace TShockAPI
|
|||
{ PacketTypes.NpcSpecial, HandleSpecial },
|
||||
{ PacketTypes.NpcAddBuff, HandleNPCAddBuff },
|
||||
{ PacketTypes.PlayerAddBuff, HandlePlayerAddBuff },
|
||||
{ PacketTypes.UpdateNPCHome, UpdateNPCHome },
|
||||
{ PacketTypes.UpdateNPCHome, HandleUpdateNPCHome },
|
||||
{ PacketTypes.SpawnBossorInvasion, HandleSpawnBoss },
|
||||
{ PacketTypes.PaintTile, HandlePaintTile },
|
||||
{ PacketTypes.PaintWall, HandlePaintWall },
|
||||
|
|
@ -148,7 +150,9 @@ namespace TShockAPI
|
|||
{ PacketTypes.ToggleParty, HandleToggleParty },
|
||||
{ PacketTypes.CrystalInvasionStart, HandleOldOnesArmy },
|
||||
{ PacketTypes.PlayerHurtV2, HandlePlayerDamageV2 },
|
||||
{ PacketTypes.PlayerDeathV2, HandlePlayerKillMeV2 }
|
||||
{ PacketTypes.PlayerDeathV2, HandlePlayerKillMeV2 },
|
||||
{ PacketTypes.FoodPlatterTryPlacing, HandleFoodPlatterTryPlacing },
|
||||
{ PacketTypes.SyncRevengeMarker, HandleSyncRevengeMarker }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1141,7 +1145,8 @@ namespace TShockAPI
|
|||
{
|
||||
Water = 0,
|
||||
Lava = 1,
|
||||
Honey = 2
|
||||
Honey = 2,
|
||||
Removal = 255 //@Olink: lets hope they never invent 255 fluids or decide to also use this :(
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1307,6 +1312,13 @@ namespace TShockAPI
|
|||
return args.Handled;
|
||||
}
|
||||
|
||||
public enum HouseholdStatus : byte
|
||||
{
|
||||
None = 0,
|
||||
Homeless = 1,
|
||||
HasRoom = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For use in a NPCHome event
|
||||
/// </summary>
|
||||
|
|
@ -1325,15 +1337,15 @@ namespace TShockAPI
|
|||
/// </summary>
|
||||
public short Y { get; set; }
|
||||
/// <summary>
|
||||
/// ByteBool homeless
|
||||
/// HouseholdStatus of the NPC
|
||||
/// </summary>
|
||||
public byte Homeless { get; set; }
|
||||
public HouseholdStatus HouseholdStatus { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// NPCHome - Called when an NPC's home is changed
|
||||
/// </summary>
|
||||
public static HandlerList<NPCHomeChangeEventArgs> NPCHome = new HandlerList<NPCHomeChangeEventArgs>();
|
||||
private static bool OnUpdateNPCHome(TSPlayer player, MemoryStream data, short id, short x, short y, byte homeless)
|
||||
private static bool OnUpdateNPCHome(TSPlayer player, MemoryStream data, short id, short x, short y, byte houseHoldStatus)
|
||||
{
|
||||
if (NPCHome == null)
|
||||
return false;
|
||||
|
|
@ -1345,7 +1357,7 @@ namespace TShockAPI
|
|||
ID = id,
|
||||
X = x,
|
||||
Y = y,
|
||||
Homeless = homeless,
|
||||
HouseholdStatus = (HouseholdStatus) houseHoldStatus,
|
||||
};
|
||||
NPCHome.Invoke(null, args);
|
||||
return args.Handled;
|
||||
|
|
@ -1853,6 +1865,52 @@ namespace TShockAPI
|
|||
return args.Handled;
|
||||
}
|
||||
|
||||
public class FoodPlatterTryPlacingEventArgs : GetDataHandledEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The X tile position of the placement action.
|
||||
/// </summary>
|
||||
public short TileX { get; set; }
|
||||
/// <summary>
|
||||
/// The Y tile position of the placement action.
|
||||
/// </summary>
|
||||
public short TileY { get; set; }
|
||||
/// <summary>
|
||||
/// The Item ID that is being placed in the plate.
|
||||
/// </summary>
|
||||
public short ItemID { get; set; }
|
||||
/// <summary>
|
||||
/// The prefix of the item that is being placed in the plate.
|
||||
/// </summary>
|
||||
public byte Prefix { get; set; }
|
||||
/// <summary>
|
||||
/// The stack of the item that is being placed in the plate.
|
||||
/// </summary>
|
||||
public short Stack { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Called when a player is placing an item in a food plate.
|
||||
/// </summary>
|
||||
public static HandlerList<FoodPlatterTryPlacingEventArgs> FoodPlatterTryPlacing = new HandlerList<FoodPlatterTryPlacingEventArgs>();
|
||||
private static bool OnFoodPlatterTryPlacing(TSPlayer player, MemoryStream data, short tileX, short tileY, short itemID, byte prefix, short stack)
|
||||
{
|
||||
if (FoodPlatterTryPlacing == null)
|
||||
return false;
|
||||
|
||||
var args = new FoodPlatterTryPlacingEventArgs
|
||||
{
|
||||
Player = player,
|
||||
Data = data,
|
||||
TileX = tileX,
|
||||
TileY = tileY,
|
||||
ItemID = itemID,
|
||||
Prefix = prefix,
|
||||
Stack = stack,
|
||||
};
|
||||
FoodPlatterTryPlacing.Invoke(null, args);
|
||||
return args.Handled;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static bool HandlePlayerInfo(GetDataHandlerArgs args)
|
||||
|
|
@ -1915,11 +1973,14 @@ namespace TShockAPI
|
|||
args.Player.TPlayer.shirtColor = shirtColor;
|
||||
args.Player.TPlayer.underShirtColor = underShirtColor;
|
||||
args.Player.TPlayer.shoeColor = shoeColor;
|
||||
//@Olink: If you need to change bool[10], please make sure you also update the for loops below to account for it.
|
||||
//There are two arrays from terraria that we only have a single array for. You will need to make sure that you are looking
|
||||
//at the correct terraria array (hideVisual or hideVisual2).
|
||||
args.Player.TPlayer.hideVisibleAccessory = new bool[10];
|
||||
for (int i = 0; i < 8; i++)
|
||||
args.Player.TPlayer.hideVisibleAccessory[i] = hideVisual[i];
|
||||
for (int i = 8; i < 10; i++)
|
||||
args.Player.TPlayer.hideVisibleAccessory[i] = hideVisual2[i];
|
||||
for (int i = 0; i < 2; i++)
|
||||
args.Player.TPlayer.hideVisibleAccessory[i+8] = hideVisual2[i];
|
||||
args.Player.TPlayer.hideMisc = hideMisc;
|
||||
args.Player.TPlayer.extraAccessory = extraSlot;
|
||||
NetMessage.SendData((int)PacketTypes.PlayerInfo, -1, args.Player.Index, NetworkText.FromLiteral(args.Player.Name), args.Player.Index);
|
||||
|
|
@ -2105,7 +2166,7 @@ namespace TShockAPI
|
|||
TShock.Log.ConsoleDebug("GetDataHandlers / HandleSpawn force teleport 'vanilla spawn' {0}", args.Player.Name);
|
||||
}
|
||||
|
||||
if ((Main.ServerSideCharacter) && (args.Player.sX > 0) && (args.Player.sY > 0) && (args.TPlayer.SpawnX > 0) && ((args.TPlayer.SpawnX != args.Player.sX) && (args.TPlayer.SpawnY != args.Player.sY)))
|
||||
else if ((Main.ServerSideCharacter) && (args.Player.sX > 0) && (args.Player.sY > 0) && (args.TPlayer.SpawnX > 0) && ((args.TPlayer.SpawnX != args.Player.sX) && (args.TPlayer.SpawnY != args.Player.sY)))
|
||||
{
|
||||
|
||||
args.Player.sX = args.TPlayer.SpawnX;
|
||||
|
|
@ -2256,6 +2317,14 @@ namespace TShockAPI
|
|||
{
|
||||
var player = args.Player;
|
||||
var size = args.Data.ReadInt16();
|
||||
|
||||
var changeType = TileChangeType.None;
|
||||
bool hasChangeType = ((size & 0x7FFF) & 0x8000) != 0;
|
||||
if (hasChangeType)
|
||||
{
|
||||
changeType = (TileChangeType)args.Data.ReadInt8();
|
||||
}
|
||||
|
||||
var tileX = args.Data.ReadInt16();
|
||||
var tileY = args.Data.ReadInt16();
|
||||
var data = args.Data;
|
||||
|
|
@ -2322,6 +2391,17 @@ namespace TShockAPI
|
|||
if (OnNewProjectile(args.Data, ident, pos, vel, knockback, dmg, owner, type, index, args.Player))
|
||||
return true;
|
||||
|
||||
lock (args.Player.RecentlyCreatedProjectiles)
|
||||
{
|
||||
if (!args.Player.RecentlyCreatedProjectiles.Any(p => p.Index == index))
|
||||
{
|
||||
args.Player.RecentlyCreatedProjectiles.Add(new GetDataHandlers.ProjectileStruct()
|
||||
{
|
||||
Index = index,
|
||||
CreatedAt = DateTime.Now
|
||||
});
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -2386,6 +2466,10 @@ namespace TShockAPI
|
|||
}
|
||||
|
||||
args.Player.LastKilledProjectile = type;
|
||||
lock (args.Player.RecentlyCreatedProjectiles)
|
||||
{
|
||||
args.Player.RecentlyCreatedProjectiles.ForEach(s => { if (s.Index == index) { s.Killed = true; } });
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2763,14 +2847,14 @@ namespace TShockAPI
|
|||
return true;
|
||||
}
|
||||
|
||||
private static bool UpdateNPCHome(GetDataHandlerArgs args)
|
||||
private static bool HandleUpdateNPCHome(GetDataHandlerArgs args)
|
||||
{
|
||||
var id = args.Data.ReadInt16();
|
||||
var x = args.Data.ReadInt16();
|
||||
var y = args.Data.ReadInt16();
|
||||
var homeless = args.Data.ReadInt8();
|
||||
var householdStatus = args.Data.ReadInt8();
|
||||
|
||||
if (OnUpdateNPCHome(args.Player, args.Data, id, x, y, homeless))
|
||||
if (OnUpdateNPCHome(args.Player, args.Data, id, x, y, householdStatus))
|
||||
return true;
|
||||
|
||||
if (!args.Player.HasPermission(Permissions.movenpc))
|
||||
|
|
@ -2827,38 +2911,53 @@ namespace TShockAPI
|
|||
string thing;
|
||||
switch (thingType)
|
||||
{
|
||||
case -14:
|
||||
thing = "has sent a request to the bunny delivery service";
|
||||
break;
|
||||
case -13:
|
||||
thing = "has sent a request to the dog delivery service";
|
||||
break;
|
||||
case -12:
|
||||
thing = "has sent a request to the cat delivery service";
|
||||
break;
|
||||
case -11:
|
||||
thing = "applied advanced combat techniques";
|
||||
break;
|
||||
case -10:
|
||||
thing = "summoned a Blood Moon";
|
||||
break;
|
||||
case -8:
|
||||
thing = "a Moon Lord";
|
||||
thing = "summoned a Moon Lord";
|
||||
break;
|
||||
case -7:
|
||||
thing = "a Martian invasion";
|
||||
thing = "summoned a Martian invasion";
|
||||
break;
|
||||
case -6:
|
||||
thing = "an eclipse";
|
||||
thing = "summoned an eclipse";
|
||||
break;
|
||||
case -5:
|
||||
thing = "a frost moon";
|
||||
thing = "summoned a frost moon";
|
||||
break;
|
||||
case -4:
|
||||
thing = "a pumpkin moon";
|
||||
thing = "summoned a pumpkin moon";
|
||||
break;
|
||||
case -3:
|
||||
thing = "the Pirates";
|
||||
thing = "summoned the Pirates";
|
||||
break;
|
||||
case -2:
|
||||
thing = "the Snow Legion";
|
||||
thing = "summoned the Snow Legion";
|
||||
break;
|
||||
case -1:
|
||||
thing = "a Goblin Invasion";
|
||||
thing = "summoned a Goblin Invasion";
|
||||
break;
|
||||
default:
|
||||
thing = String.Format("the {0}", npc.FullName);
|
||||
thing = String.Format("summoned the {0}", npc.FullName);
|
||||
break;
|
||||
}
|
||||
if (TShock.Config.AnonymousBossInvasions)
|
||||
TShock.Utils.SendLogs(string.Format("{0} summoned {1}!", args.Player.Name, thing), Color.PaleVioletRed, args.Player);
|
||||
TShock.Utils.SendLogs(string.Format("{0} {1}!", args.Player.Name, thing), Color.PaleVioletRed, args.Player);
|
||||
else
|
||||
TShock.Utils.Broadcast(String.Format("{0} summoned {1}!", args.Player.Name, thing), 175, 75, 255);
|
||||
TShock.Utils.Broadcast(String.Format("{0} {1}!", args.Player.Name, thing), 175, 75, 255);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -3084,6 +3183,150 @@ namespace TShockAPI
|
|||
|
||||
private static bool HandleLoadNetModule(GetDataHandlerArgs args)
|
||||
{
|
||||
short moduleId = args.Data.ReadInt16();
|
||||
if (moduleId == (int)NetModulesTypes.CreativePowers)
|
||||
{
|
||||
CreativePowerTypes powerId = (CreativePowerTypes)args.Data.ReadInt16();
|
||||
switch (powerId)
|
||||
{
|
||||
case CreativePowerTypes.FreezeTime:
|
||||
{
|
||||
if (!args.Player.HasPermission(Permissions.journey_timefreeze))
|
||||
{
|
||||
args.Player.SendErrorMessage("You don't have permission to freeze the time of the server!");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CreativePowerTypes.SetDawn:
|
||||
case CreativePowerTypes.SetNoon:
|
||||
case CreativePowerTypes.SetDusk:
|
||||
case CreativePowerTypes.SetMidnight:
|
||||
{
|
||||
if (!args.Player.HasPermission(Permissions.journey_timeset))
|
||||
{
|
||||
args.Player.SendErrorMessage("You don't have permission to modify the time of the server!");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CreativePowerTypes.Godmode:
|
||||
{
|
||||
if (!args.Player.HasPermission(Permissions.journey_godmode))
|
||||
{
|
||||
args.Player.SendErrorMessage("You don't have permission to toggle godmode!");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CreativePowerTypes.WindStrength:
|
||||
{
|
||||
if (!args.Player.HasPermission(Permissions.journey_windstrength))
|
||||
{
|
||||
args.Player.SendErrorMessage("You don't have permission to modify the wind strength of the server!");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CreativePowerTypes.RainStrength:
|
||||
{
|
||||
if (!args.Player.HasPermission(Permissions.journey_rainstrength))
|
||||
{
|
||||
args.Player.SendErrorMessage("You don't have permission to modify the rain strength of the server!");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CreativePowerTypes.TimeSpeed:
|
||||
{
|
||||
if (!args.Player.HasPermission(Permissions.journey_timespeed))
|
||||
{
|
||||
args.Player.SendErrorMessage("You don't have permission to modify the time speed of the server!");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CreativePowerTypes.RainFreeze:
|
||||
{
|
||||
if (!args.Player.HasPermission(Permissions.journey_rainfreeze))
|
||||
{
|
||||
args.Player.SendErrorMessage("You don't have permission to freeze the rain strength of the server!");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CreativePowerTypes.WindFreeze:
|
||||
{
|
||||
if (!args.Player.HasPermission(Permissions.journey_windfreeze))
|
||||
{
|
||||
args.Player.SendErrorMessage("You don't have permission to freeze the wind strength of the server!");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CreativePowerTypes.IncreasePlacementRange:
|
||||
{
|
||||
if (!args.Player.HasPermission(Permissions.journey_placementrange))
|
||||
{
|
||||
args.Player.SendErrorMessage("You don't have permission to modify the tile placement range of your character!");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CreativePowerTypes.WorldDifficulty:
|
||||
{
|
||||
if (!args.Player.HasPermission(Permissions.journey_setdifficulty))
|
||||
{
|
||||
args.Player.SendErrorMessage("You don't have permission to modify the world difficulty of the server!");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CreativePowerTypes.BiomeSpreadFreeze:
|
||||
{
|
||||
if (!args.Player.HasPermission(Permissions.journey_biomespreadfreeze))
|
||||
{
|
||||
args.Player.SendErrorMessage("You don't have permission to freeze the biome spread of the server!");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CreativePowerTypes.SetSpawnRate:
|
||||
{
|
||||
// This is a monkeypatch because the 1.4.0.4 seemingly at random sends NPC spawn rate changes even outside of journey mode
|
||||
// (with SSC on) -- particles, May 25, 2 Reiwa
|
||||
if (!Main.GameModeInfo.IsJourneyMode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!args.Player.HasPermission(Permissions.journey_setspawnrate))
|
||||
{
|
||||
args.Player.SendErrorMessage("You don't have permission to modify the NPC spawn rate of the server!");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (moduleId == (int)NetModulesTypes.CreativeUnlocksPlayerReport && Main.GameModeInfo.IsJourneyMode)
|
||||
{
|
||||
var unknownField = args.Data.ReadByte();
|
||||
|
||||
if (unknownField == 0) //this is required or something???
|
||||
{
|
||||
var itemId = args.Data.ReadUInt16();
|
||||
var amount = args.Data.ReadUInt16();
|
||||
|
||||
var totalSacrificed = TShock.ResearchDatastore.SacrificeItem(itemId, amount, args.Player);
|
||||
|
||||
var response = NetCreativeUnlocksModule.SerializeItemSacrifice(itemId, totalSacrificed);
|
||||
NetManager.Instance.Broadcast(response);
|
||||
}
|
||||
}
|
||||
|
||||
// As of 1.4.x.x, this is now used for more things:
|
||||
// NetCreativePowersModule
|
||||
// NetCreativePowerPermissionsModule
|
||||
|
|
@ -3141,24 +3384,38 @@ namespace TShockAPI
|
|||
private static bool HandleSyncExtraValue(GetDataHandlerArgs args)
|
||||
{
|
||||
var npcIndex = args.Data.ReadInt16();
|
||||
var extraValue = args.Data.ReadSingle();
|
||||
var extraValue = args.Data.ReadInt32();
|
||||
var position = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle());
|
||||
|
||||
if (position.X < 0 || position.X >= Main.maxTilesX || position.Y < 0 || position.Y >= Main.maxTilesY)
|
||||
if (position.X < 0 || position.X >= (Main.maxTilesX * 16.0f) || position.Y < 0 || position.Y >= (Main.maxTilesY * 16.0f))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("GetDataHandlers / HandleSyncExtraValue rejected extents check {0}", args.Player.Name);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Main.expertMode)
|
||||
if (!Main.expertMode && !Main.masterMode)
|
||||
{
|
||||
TShock.Log.ConsoleDebug("GetDataHandlers / HandleSyncExtraValue rejected expert mode check {0}", args.Player.Name);
|
||||
TShock.Log.ConsoleDebug("GetDataHandlers / HandleSyncExtraValue rejected expert/master mode check {0}", args.Player.Name);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!args.Player.IsInRange((int)position.X, (int)position.Y))
|
||||
if (npcIndex < 0 || npcIndex >= Main.npc.Length)
|
||||
{
|
||||
TShock.Log.ConsoleDebug("GetDataHandlers / HandleSyncExtraValue rejected range check {0}", args.Player.Name);
|
||||
TShock.Log.ConsoleDebug("GetDataHandlers / HandleSyncExtraValue rejected npc id out of bounds check - NPC ID: {0}", npcIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
var npc = Main.npc[npcIndex];
|
||||
if (npc == null)
|
||||
{
|
||||
TShock.Log.ConsoleDebug("GetDataHandlers / HandleSyncExtraValue rejected npc is null - NPC ID: {0}", npcIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
var distanceFromCoinPacketToNpc = Utils.Distance(position, npc.position);
|
||||
if (distanceFromCoinPacketToNpc >= (5*16f)) //5 tile range
|
||||
{
|
||||
TShock.Log.ConsoleDebug("GetDataHandlers / HandleSyncExtraValue rejected range check {0},{1} vs {2},{3} which is {4}", npc.position.X, npc.position.Y, position.X, position.Y, distanceFromCoinPacketToNpc);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -3337,7 +3594,7 @@ namespace TShockAPI
|
|||
}
|
||||
|
||||
// Handle kicks/bans on mediumcore/hardcore deaths.
|
||||
if (args.TPlayer.difficulty != 0) // Player is not softcore
|
||||
if (args.TPlayer.difficulty == 1 || args.TPlayer.difficulty == 2) // Player is not softcore
|
||||
{
|
||||
bool mediumcore = args.TPlayer.difficulty == 1;
|
||||
bool shouldBan = mediumcore ? TShock.Config.BanOnMediumcoreDeath : TShock.Config.BanOnHardcoreDeath;
|
||||
|
|
@ -3372,7 +3629,34 @@ namespace TShockAPI
|
|||
|
||||
return false;
|
||||
}
|
||||
private static bool HandleFoodPlatterTryPlacing(GetDataHandlerArgs args)
|
||||
{
|
||||
short tileX = args.Data.ReadInt16();
|
||||
short tileY = args.Data.ReadInt16();
|
||||
short itemID = args.Data.ReadInt16();
|
||||
byte prefix = args.Data.ReadInt8();
|
||||
short stack = args.Data.ReadInt16();
|
||||
|
||||
if (OnFoodPlatterTryPlacing(args.Player, args.Data, tileX, tileY, itemID, prefix, stack))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HandleSyncRevengeMarker(GetDataHandlerArgs args)
|
||||
{
|
||||
int uniqueID = args.Data.ReadInt32();
|
||||
Vector2 location = args.Data.ReadVector2();
|
||||
int netId = args.Data.ReadInt32();
|
||||
float npcHpPercent = args.Data.ReadSingle();
|
||||
int npcTypeAgainstDiscouragement = args.Data.ReadInt32(); //tfw the argument is Type Against Discouragement
|
||||
int npcAiStyleAgainstDiscouragement = args.Data.ReadInt32(); //see ^
|
||||
int coinsValue = args.Data.ReadInt32();
|
||||
float baseValue = args.Data.ReadSingle();
|
||||
bool spawnedFromStatus = args.Data.ReadBoolean();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public enum EditAction
|
||||
{
|
||||
|
|
@ -3446,6 +3730,30 @@ namespace TShockAPI
|
|||
{ ProjectileID.MysticSnakeCoil, TileID.MysticSnakeRope }
|
||||
};
|
||||
|
||||
internal static Dictionary<int, LiquidType> projectileCreatesLiquid = new Dictionary<int, LiquidType>
|
||||
{
|
||||
{ProjectileID.LavaBomb, LiquidType.Lava},
|
||||
{ProjectileID.LavaRocket, LiquidType.Lava },
|
||||
{ProjectileID.LavaGrenade, LiquidType.Lava },
|
||||
{ProjectileID.LavaMine, LiquidType.Lava },
|
||||
//{ProjectileID.LavaSnowmanRocket, LiquidType.Lava }, //these require additional checks.
|
||||
{ProjectileID.WetBomb, LiquidType.Water},
|
||||
{ProjectileID.WetRocket, LiquidType.Water },
|
||||
{ProjectileID.WetGrenade, LiquidType.Water},
|
||||
{ProjectileID.WetMine, LiquidType.Water},
|
||||
//{ProjectileID.WetSnowmanRocket, LiquidType.Water}, //these require additional checks.
|
||||
{ProjectileID.HoneyBomb, LiquidType.Honey},
|
||||
{ProjectileID.HoneyRocket, LiquidType.Honey },
|
||||
{ProjectileID.HoneyGrenade, LiquidType.Honey },
|
||||
{ProjectileID.HoneyMine, LiquidType.Honey },
|
||||
//{ProjectileID.HoneySnowmanRocket, LiquidType.Honey }, //these require additional checks.
|
||||
{ProjectileID.DryBomb, LiquidType.Removal },
|
||||
{ProjectileID.DryRocket, LiquidType.Removal },
|
||||
{ProjectileID.DryGrenade, LiquidType.Removal },
|
||||
{ProjectileID.DryMine, LiquidType.Removal },
|
||||
//{ProjectileID.DrySnowmanRocket, LiquidType.Removal } //these require additional checks.
|
||||
};
|
||||
|
||||
internal static Dictionary<int, int> ropeCoilPlacements = new Dictionary<int, int>
|
||||
{
|
||||
{ItemID.RopeCoil, TileID.Rope},
|
||||
|
|
@ -3461,5 +3769,46 @@ namespace TShockAPI
|
|||
{
|
||||
{TileID.MinecartTrack, 3}
|
||||
};
|
||||
|
||||
internal struct ProjectileStruct
|
||||
{
|
||||
public int Index { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public bool Killed { get; internal set; }
|
||||
}
|
||||
|
||||
public enum NetModulesTypes
|
||||
{
|
||||
Liquid,
|
||||
Text,
|
||||
Ping,
|
||||
Ambience,
|
||||
Bestiary,
|
||||
CreativeUnlocks,
|
||||
CreativePowers,
|
||||
CreativeUnlocksPlayerReport,
|
||||
TeleportPylon,
|
||||
Particles,
|
||||
CreativePowerPermissions
|
||||
}
|
||||
|
||||
public enum CreativePowerTypes
|
||||
{
|
||||
FreezeTime,
|
||||
SetDawn,
|
||||
SetNoon,
|
||||
SetDusk,
|
||||
SetMidnight,
|
||||
Godmode,
|
||||
WindStrength,
|
||||
RainStrength,
|
||||
TimeSpeed,
|
||||
RainFreeze,
|
||||
WindFreeze,
|
||||
IncreasePlacementRange,
|
||||
WorldDifficulty,
|
||||
BiomeSpreadFreeze,
|
||||
SetSpawnRate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace TShockAPI.Net
|
|||
public short FrameX { get; set; }
|
||||
public short FrameY { get; set; }
|
||||
public bool Lighted { get; set; }
|
||||
public byte Wall { get; set; }
|
||||
public ushort Wall { get; set; }
|
||||
public byte Liquid { get; set; }
|
||||
public byte LiquidType { get; set; }
|
||||
public bool Wire { get; set; }
|
||||
|
|
@ -175,7 +175,7 @@ namespace TShockAPI.Net
|
|||
}
|
||||
|
||||
if (HasWall)
|
||||
stream.WriteInt8(Wall);
|
||||
stream.WriteInt16((short)Wall);;
|
||||
|
||||
if (HasLiquid)
|
||||
{
|
||||
|
|
@ -218,7 +218,7 @@ namespace TShockAPI.Net
|
|||
|
||||
if (flags[2])
|
||||
{
|
||||
Wall = stream.ReadInt8();
|
||||
Wall = stream.ReadUInt16();
|
||||
}
|
||||
|
||||
if (flags[3])
|
||||
|
|
|
|||
|
|
@ -31,8 +31,7 @@ namespace TShockAPI
|
|||
/// <summary>Contains the permission nodes used in TShock.</summary>
|
||||
public static class Permissions
|
||||
{
|
||||
// tshock.account nodes
|
||||
|
||||
#region tshock.account nodes
|
||||
[Description("User can register account in game.")]
|
||||
public static readonly string canregister = "tshock.account.register";
|
||||
|
||||
|
|
@ -44,9 +43,9 @@ namespace TShockAPI
|
|||
|
||||
[Description("User can change password in game.")]
|
||||
public static readonly string canchangepassword = "tshock.account.changepassword";
|
||||
#endregion
|
||||
|
||||
// tshock.admin nodes
|
||||
|
||||
#region tshock.admin nodes
|
||||
[Description("User can set build protection status.")]
|
||||
public static readonly string antibuild = "tshock.admin.antibuild";
|
||||
|
||||
|
|
@ -106,17 +105,17 @@ namespace TShockAPI
|
|||
|
||||
[Description("User can get other users' info.")]
|
||||
public static readonly string userinfo = "tshock.admin.userinfo";
|
||||
#endregion
|
||||
|
||||
// tshock.buff nodes
|
||||
|
||||
#region tshock.buff nodes
|
||||
[Description("User can buff self.")]
|
||||
public static readonly string buff = "tshock.buff.self";
|
||||
|
||||
[Description("User can buff other players.")]
|
||||
public static readonly string buffplayer = "tshock.buff.others";
|
||||
#endregion
|
||||
|
||||
// tshock.cfg nodes
|
||||
|
||||
#region tshock.cfg nodes
|
||||
[Description("User is notified when an update is available, user can turn off / restart the server.")]
|
||||
public static readonly string maintenance = "tshock.cfg.maintenance";
|
||||
|
||||
|
|
@ -131,9 +130,9 @@ namespace TShockAPI
|
|||
|
||||
[Description("User can create reference files of Terraria IDs and the permission matrix in the server folder.")]
|
||||
public static readonly string createdumps = "tshock.cfg.createdumps";
|
||||
#endregion
|
||||
|
||||
// tshock.ignore nodes
|
||||
|
||||
#region tshock.ignore nodes
|
||||
[Description("Prevents you from being reverted by kill tile abuse detection.")]
|
||||
public static readonly string ignorekilltiledetection = "tshock.ignore.removetile";
|
||||
|
||||
|
|
@ -169,9 +168,9 @@ namespace TShockAPI
|
|||
|
||||
[Description("Prevents you from being disabled by abnormal MP.")]
|
||||
public static readonly string ignoremp = "tshock.ignore.mp";
|
||||
#endregion
|
||||
|
||||
// tshock.item nodes
|
||||
|
||||
#region tshock.item nodes
|
||||
[Description("User can give items.")]
|
||||
public static readonly string give = "tshock.item.give";
|
||||
|
||||
|
|
@ -180,9 +179,9 @@ namespace TShockAPI
|
|||
|
||||
[Description("Allows you to use banned items.")]
|
||||
public static readonly string usebanneditem = "tshock.item.usebanned";
|
||||
#endregion
|
||||
|
||||
// tshock.npc nodes
|
||||
|
||||
#region tshock.npc nodes
|
||||
[Description("User can edit the max spawns.")]
|
||||
public static readonly string maxspawns = "tshock.npc.maxspawns";
|
||||
|
||||
|
|
@ -227,9 +226,9 @@ namespace TShockAPI
|
|||
|
||||
[Description("Allows a user to elevate to superadmin for 10 minutes.")]
|
||||
public static readonly string su = "tshock.su";
|
||||
#endregion
|
||||
|
||||
// tshock.tp nodes
|
||||
|
||||
#region tshock.tp nodes
|
||||
[Description("User can teleport *everyone* to them.")]
|
||||
public static readonly string tpallothers = "tshock.tp.allothers";
|
||||
|
||||
|
|
@ -268,9 +267,9 @@ namespace TShockAPI
|
|||
|
||||
[Description("User can use wormhole potions.")]
|
||||
public static readonly string wormhole = "tshock.tp.wormhole";
|
||||
#endregion
|
||||
|
||||
// tshock.world nodes
|
||||
|
||||
#region tshock.world nodes
|
||||
[Description("User can use the 'worldevent' command")]
|
||||
public static readonly string manageevents = "tshock.world.events";
|
||||
|
||||
|
|
@ -372,9 +371,47 @@ namespace TShockAPI
|
|||
|
||||
[Description("Player can toggle party event.")]
|
||||
public static readonly string toggleparty = "tshock.world.toggleparty";
|
||||
#endregion
|
||||
|
||||
// Non-grouped
|
||||
#region tshock.journey nodes
|
||||
[Description("User can use Creative UI freeze time.")]
|
||||
public static readonly string journey_timefreeze = "tshock.journey.time.freeze";
|
||||
|
||||
[Description("User can use Creative UI to set world time.")]
|
||||
public static readonly string journey_timeset = "tshock.journey.time.set";
|
||||
|
||||
[Description("User can use Creative UI to set world time speed.")]
|
||||
public static readonly string journey_timespeed = "tshock.journey.time.setspeed";
|
||||
|
||||
[Description("User can use Creative UI to to toggle character godmode.")]
|
||||
public static readonly string journey_godmode = "tshock.journey.godmode";
|
||||
|
||||
[Description("User can use Creative UI to set world wind strength/seed.")]
|
||||
public static readonly string journey_windstrength = "tshock.journey.wind.strength";
|
||||
|
||||
[Description("User can use Creative UI to stop the world wind strength from changing.")]
|
||||
public static readonly string journey_windfreeze = "tshock.journey.wind.freeze";
|
||||
|
||||
[Description("User can use Creative UI to set world rain strength/seed.")]
|
||||
public static readonly string journey_rainstrength = "tshock.journey.rain.strength";
|
||||
|
||||
[Description("User can use Creative UI to stop the world rain strength from changing.")]
|
||||
public static readonly string journey_rainfreeze = "tshock.journey.rain.freeze";
|
||||
|
||||
[Description("User can use Creative UI to toggle increased placement range.")]
|
||||
public static readonly string journey_placementrange = "tshock.journey.placementrange";
|
||||
|
||||
[Description("User can use Creative UI to set world difficulty/mode.")]
|
||||
public static readonly string journey_setdifficulty = "tshock.journey.setdifficulty";
|
||||
|
||||
[Description("User can use Creative UI to stop the biome spread of the world.")]
|
||||
public static readonly string journey_biomespreadfreeze = "tshock.journey.biomespreadfreeze";
|
||||
|
||||
[Description("User can use Creative UI to set the NPC spawn rate of the world.")]
|
||||
public static readonly string journey_setspawnrate = "tshock.journey.setspawnrate";
|
||||
#endregion
|
||||
|
||||
#region Non-grouped
|
||||
[Description("User can clear items or projectiles.")]
|
||||
public static readonly string clear = "tshock.clear";
|
||||
|
||||
|
|
@ -429,6 +466,9 @@ namespace TShockAPI
|
|||
[Description("Player can see advanced information about any user account.")]
|
||||
public static readonly string advaccountinfo = "tshock.accountinfo.details";
|
||||
|
||||
[Description("Player can resync themselves with server state.")]
|
||||
public static readonly string synclocalarea = "tshock.synclocalarea";
|
||||
#endregion
|
||||
/// <summary>
|
||||
/// Lists all commands associated with a given permission
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ using Microsoft.Xna.Framework;
|
|||
using Terraria;
|
||||
using TShockAPI;
|
||||
using Terraria.Localization;
|
||||
using Terraria.GameContent.NetModules;
|
||||
using Terraria.Net;
|
||||
using Terraria.ID;
|
||||
|
||||
namespace TShockAPI
|
||||
{
|
||||
|
|
@ -480,6 +483,22 @@ namespace TShockAPI
|
|||
NetMessage.SendData(76, -1, -1, NetworkText.Empty, player.Index);
|
||||
|
||||
NetMessage.SendData(39, player.Index, -1, NetworkText.Empty, 400);
|
||||
|
||||
if (Main.GameModeInfo.IsJourneyMode)
|
||||
{
|
||||
var sacrificedItems = TShock.ResearchDatastore.GetSacrificedItems();
|
||||
for(int i = 0; i < ItemID.Count; i++)
|
||||
{
|
||||
var amount = 0;
|
||||
if (sacrificedItems.ContainsKey(i))
|
||||
{
|
||||
amount = sacrificedItems[i];
|
||||
}
|
||||
|
||||
var response = NetCreativeUnlocksModule.SerializeItemSacrifice(i, amount);
|
||||
NetManager.Instance.SendToClient(response, player.TPlayer.whoAmI);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -685,7 +685,7 @@ namespace TShockAPI
|
|||
/// <returns>True if they can paint.</returns>
|
||||
public bool HasPaintPermission(int x, int y)
|
||||
{
|
||||
return HasBuildPermission(x, y) || HasPermission(Permissions.canpaint);
|
||||
return HasBuildPermission(x, y) && HasPermission(Permissions.canpaint);
|
||||
}
|
||||
|
||||
/// <summary>Checks if a player can place ice, and if they can, tracks ice placements and removals.</summary>
|
||||
|
|
@ -764,6 +764,12 @@ namespace TShockAPI
|
|||
/// </summary>
|
||||
public int LastKilledProjectile = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Keeps track of recently created projectiles by this player. TShock.cs OnSecondUpdate() removes from this in an async task.
|
||||
/// Projectiles older than 5 seconds are purged from this collection as they are no longer "recent."
|
||||
/// </summary>
|
||||
internal List<TShockAPI.GetDataHandlers.ProjectileStruct> RecentlyCreatedProjectiles = new List<TShockAPI.GetDataHandlers.ProjectileStruct>();
|
||||
|
||||
/// <summary>
|
||||
/// The current region this player is in, or null if none.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -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 = "Go to sleep Patrikkk, Icy, Chris, Death, Axeel, Zaicon, hakusaro, and Yoraiz0r <3";
|
||||
public static readonly string VersionCodename = "Go to sleep Patrikkk, Icy, Chris, Death, Axeel, Zaicon, hakusaro, Zack, and Yoraiz0r <3";
|
||||
|
||||
/// <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";
|
||||
|
|
@ -100,6 +100,8 @@ namespace TShockAPI
|
|||
public static RememberedPosManager RememberedPos;
|
||||
/// <summary>CharacterDB - Static reference to the SSC character manager.</summary>
|
||||
public static CharacterManager CharacterDB;
|
||||
/// <summary>Contains the information about what research has been performed in Journey mode.</summary>
|
||||
public static ResearchDatastore ResearchDatastore;
|
||||
/// <summary>Config - Static reference to the config system, for accessing values set in users' config files.</summary>
|
||||
public static ConfigFile Config { get; set; }
|
||||
/// <summary>ServerSideCharacterConfig - Static reference to the server side character config, for accessing values set by users to modify SSC.</summary>
|
||||
|
|
@ -324,6 +326,7 @@ namespace TShockAPI
|
|||
TileBans = new TileManager(DB);
|
||||
RememberedPos = new RememberedPosManager(DB);
|
||||
CharacterDB = new CharacterManager(DB);
|
||||
ResearchDatastore = new ResearchDatastore(DB);
|
||||
RestApi = new SecureRest(Netplay.ServerIP, Config.RestApiPort);
|
||||
RestManager = new RestManager(RestApi);
|
||||
RestManager.RegisterRestfulCommands();
|
||||
|
|
@ -686,6 +689,16 @@ namespace TShockAPI
|
|||
}
|
||||
})
|
||||
|
||||
.AddFlag("-worldselectpath", pathChecker)
|
||||
.After(() =>
|
||||
{
|
||||
if (path != null)
|
||||
{
|
||||
Main.WorldPath = path;
|
||||
ServerApi.LogWriter.PluginWriteLine(this, "World path has been set to " + path, TraceLevel.Info);
|
||||
}
|
||||
})
|
||||
|
||||
.AddFlag("-logpath", pathChecker)
|
||||
.After(() =>
|
||||
{
|
||||
|
|
@ -1065,6 +1078,8 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bouncer.OnSecondUpdate();
|
||||
Utils.SetConsoleTitle(false);
|
||||
}
|
||||
|
||||
|
|
@ -1478,8 +1493,6 @@ namespace TShockAPI
|
|||
|
||||
PacketTypes type = e.MsgID;
|
||||
|
||||
Log.ConsoleDebug("Recv: {0:X}: {2} ({1:XX})", e.Msg.whoAmI, (byte)type, type);
|
||||
|
||||
var player = Players[e.Msg.whoAmI];
|
||||
if (player == null || !player.ConnectionAlive)
|
||||
{
|
||||
|
|
@ -1621,6 +1634,32 @@ namespace TShockAPI
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if (e.MsgId == PacketTypes.ProjectileNew)
|
||||
{
|
||||
if (e.number >= 0 && e.number < Main.projectile.Length)
|
||||
{
|
||||
var projectile = Main.projectile[e.number];
|
||||
if (projectile.active && projectile.owner >= 0 && GetDataHandlers.projectileCreatesLiquid.ContainsKey(projectile.type))
|
||||
{
|
||||
var player = Players[projectile.owner];
|
||||
if (player != null)
|
||||
{
|
||||
if (player.RecentlyCreatedProjectiles.Any(p => p.Index == e.number && p.Killed))
|
||||
{
|
||||
player.RecentlyCreatedProjectiles.RemoveAll(p => p.Index == e.number && p.Killed);
|
||||
}
|
||||
|
||||
if (!player.RecentlyCreatedProjectiles.Any(p => p.Index == e.number)) {
|
||||
player.RecentlyCreatedProjectiles.Add(new GetDataHandlers.ProjectileStruct()
|
||||
{
|
||||
Index = e.number,
|
||||
CreatedAt = DateTime.Now
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>OnStartHardMode - Fired when hard mode is started.</summary>
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@
|
|||
<Compile Include="CLI\FlagSet.cs" />
|
||||
<Compile Include="DB\ProjectileManager.cs" />
|
||||
<Compile Include="DB\RegionManager.cs" />
|
||||
<Compile Include="DB\ResearchDatastore.cs" />
|
||||
<Compile Include="DB\TileManager.cs" />
|
||||
<Compile Include="Extensions\ExceptionExt.cs" />
|
||||
<Compile Include="Hooks\AccountHooks.cs" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue