Merge branch 'general-devel' into handlesynctilepicking
This commit is contained in:
commit
60840807cf
20 changed files with 832 additions and 180 deletions
|
|
@ -3,6 +3,12 @@
|
|||
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 Changes
|
||||
* New permission `tshock.tp.pylon` to enable teleporting via Teleportation Pylons (@QuiCM)
|
||||
* New permission `tshock.journey.research` to enable sharing research via item sacrifice (@QuiCM)
|
||||
* Add Emoji event to GetDataHandler. This packet is received when a player tries to display an emote.
|
||||
* Adding EmojiHandler to handle an exploit. Adding `tshock.sendemoji` permission and checks. Added this permission to guest group by default.
|
||||
* Handling SyncCavernMonsterType packet to prevent an exploit where players could modify the server's cavern monster types and make the server spawn any NPCs - including bosses - onto other players.
|
||||
* Added LandGolfBallInCup event which is accessible for developers to work with, as well as LandGolfBallInCup handler to handle exploits where players could send direct packets to trigger and imitate golf ball cup landing anywhere in the game world. Added two public lists in Handlers.LandGolfBallInCupHandler: GolfBallProjectileIDs and GolfClubItemIDs. (@Patrikkk)
|
||||
* Add SyncTilePicking event. This is called when a player damages a tile.
|
||||
|
||||
## TShock 4.4.0 (Pre-release 10)
|
||||
|
|
|
|||
|
|
@ -37,13 +37,25 @@ namespace TShockAPI
|
|||
internal sealed class Bouncer
|
||||
{
|
||||
internal Handlers.SendTileSquareHandler STSHandler { get; set; }
|
||||
internal Handlers.NetModules.NetModulePacketHandler NetModuleHandler { get; set; }
|
||||
internal Handlers.EmojiHandler EmojiHandler { get; set; }
|
||||
internal Handlers.LandGolfBallInCupHandler LandGolfBallInCupHandler { get; set; }
|
||||
|
||||
/// <summary>Constructor call initializes Bouncer and related functionality.</summary>
|
||||
/// <returns>A new Bouncer.</returns>
|
||||
internal Bouncer()
|
||||
{
|
||||
STSHandler = new Handlers.SendTileSquareHandler();
|
||||
GetDataHandlers.SendTileSquare += STSHandler.OnReceiveSendTileSquare;
|
||||
GetDataHandlers.SendTileSquare += STSHandler.OnReceive;
|
||||
|
||||
NetModuleHandler = new Handlers.NetModules.NetModulePacketHandler();
|
||||
GetDataHandlers.ReadNetModule += NetModuleHandler.OnReceive;
|
||||
|
||||
EmojiHandler = new Handlers.EmojiHandler();
|
||||
GetDataHandlers.Emoji += EmojiHandler.OnReceive;
|
||||
|
||||
LandGolfBallInCupHandler = new Handlers.LandGolfBallInCupHandler();
|
||||
GetDataHandlers.LandGolfBallInCup += LandGolfBallInCupHandler.OnReceive;
|
||||
|
||||
// Setup hooks
|
||||
GetDataHandlers.GetSection += OnGetSection;
|
||||
|
|
|
|||
|
|
@ -1987,7 +1987,7 @@ namespace TShockAPI
|
|||
|
||||
void FailedPermissionCheck()
|
||||
{
|
||||
args.Player.SendErrorMessage("You do not have sufficient permissions to start the {0} event.", eventType);
|
||||
args.Player.SendErrorMessage("You do not have permission to start the {0} event.", eventType);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -4873,7 +4873,7 @@ namespace TShockAPI
|
|||
{
|
||||
if (!args.Player.HasPermission(Permissions.tp))
|
||||
{
|
||||
args.Player.SendErrorMessage("You don't have the necessary permission to do that.");
|
||||
args.Player.SendErrorMessage("You do not have permission to teleport.");
|
||||
break;
|
||||
}
|
||||
if (args.Parameters.Count <= 1)
|
||||
|
|
@ -5051,7 +5051,7 @@ namespace TShockAPI
|
|||
}
|
||||
if (displayIdsRequested && !args.Player.HasPermission(Permissions.seeids))
|
||||
{
|
||||
args.Player.SendErrorMessage("You don't have the required permission to list player ids.");
|
||||
args.Player.SendErrorMessage("You do not have permission to list player ids.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -6025,7 +6025,7 @@ namespace TShockAPI
|
|||
{
|
||||
if (!args.Player.HasPermission(Permissions.godmodeother))
|
||||
{
|
||||
args.Player.SendErrorMessage("You do not have permission to god mode another player!");
|
||||
args.Player.SendErrorMessage("You do not have permission to god mode another player.");
|
||||
return;
|
||||
}
|
||||
string plStr = String.Join(" ", args.Parameters);
|
||||
|
|
|
|||
|
|
@ -65,7 +65,8 @@ namespace TShockAPI.DB
|
|||
Permissions.canpartychat,
|
||||
Permissions.cantalkinthird,
|
||||
Permissions.canchat,
|
||||
Permissions.synclocalarea));
|
||||
Permissions.synclocalarea,
|
||||
Permissions.sendemoji));
|
||||
|
||||
AddDefaultGroup("default", "guest",
|
||||
string.Join(",",
|
||||
|
|
|
|||
|
|
@ -151,10 +151,13 @@ namespace TShockAPI
|
|||
{ PacketTypes.CrystalInvasionStart, HandleOldOnesArmy },
|
||||
{ PacketTypes.PlayerHurtV2, HandlePlayerDamageV2 },
|
||||
{ PacketTypes.PlayerDeathV2, HandlePlayerKillMeV2 },
|
||||
{ PacketTypes.Emoji, HandleEmoji },
|
||||
{ PacketTypes.SyncTilePicking, HandleSyncTilePicking },
|
||||
{ PacketTypes.SyncRevengeMarker, HandleSyncRevengeMarker },
|
||||
{ PacketTypes.LandGolfBallInCup, HandleLandGolfBallInCup },
|
||||
{ PacketTypes.FishOutNPC, HandleFishOutNPC },
|
||||
{ PacketTypes.FoodPlatterTryPlacing, HandleFoodPlatterTryPlacing }
|
||||
{ PacketTypes.FoodPlatterTryPlacing, HandleFoodPlatterTryPlacing },
|
||||
{ PacketTypes.SyncCavernMonsterType, HandleSyncCavernMonsterType }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1900,8 +1903,6 @@ namespace TShockAPI
|
|||
|
||||
var args = new SyncTilePickingEventArgs
|
||||
{
|
||||
Player = player,
|
||||
Data = data,
|
||||
PlayerIndex = playerIndex,
|
||||
TileX = tileX,
|
||||
TileY = tileY,
|
||||
|
|
@ -1912,6 +1913,89 @@ namespace TShockAPI
|
|||
}
|
||||
|
||||
|
||||
/// For use in an Emoji event.
|
||||
/// </summary>
|
||||
public class EmojiEventArgs : GetDataHandledEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The player index in the packet, who sends the emoji.
|
||||
/// </summary>
|
||||
public byte PlayerIndex { get; set; }
|
||||
/// <summary>
|
||||
/// The ID of the emoji, that is being received.
|
||||
/// </summary>
|
||||
public byte EmojiID { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Called when a player sends an emoji.
|
||||
/// </summary>
|
||||
public static HandlerList<EmojiEventArgs> Emoji = new HandlerList<EmojiEventArgs>();
|
||||
private static bool OnEmoji(TSPlayer player, MemoryStream data, byte playerIndex, byte emojiID)
|
||||
{
|
||||
if (Emoji == null)
|
||||
return false;
|
||||
|
||||
var args = new EmojiEventArgs
|
||||
{
|
||||
Player = player,
|
||||
Data = data,
|
||||
PlayerIndex = playerIndex,
|
||||
EmojiID = emojiID
|
||||
};
|
||||
Emoji.Invoke(null, args);
|
||||
return args.Handled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For use in a LandBallInCup event.
|
||||
/// </summary>
|
||||
public class LandGolfBallInCupEventArgs : GetDataHandledEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The player index in the packet, who puts the ball in the cup.
|
||||
/// </summary>
|
||||
public byte PlayerIndex { get; set; }
|
||||
/// <summary>
|
||||
/// The X tile position of where the ball lands in a cup.
|
||||
/// </summary>
|
||||
public ushort TileX { get; set; }
|
||||
/// <summary>
|
||||
/// The Y tile position of where the ball lands in a cup.
|
||||
/// </summary>
|
||||
public ushort TileY { get; set; }
|
||||
/// <summary>
|
||||
/// The amount of hits it took for the player to land the ball in the cup.
|
||||
/// </summary>
|
||||
public ushort Hits { get; set; }
|
||||
/// <summary>
|
||||
/// The type of the projectile that was landed in the cup. A golfball in legit cases.
|
||||
/// </summary>
|
||||
public ushort ProjectileType { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a player lands a golf ball in a cup.
|
||||
/// </summary>
|
||||
public static HandlerList<LandGolfBallInCupEventArgs> LandGolfBallInCup = new HandlerList<LandGolfBallInCupEventArgs>();
|
||||
private static bool OnLandGolfBallInCup(TSPlayer player, MemoryStream data, byte playerIndex, ushort tileX, ushort tileY, ushort hits, ushort projectileType )
|
||||
{
|
||||
if (LandGolfBallInCup == null)
|
||||
return false;
|
||||
|
||||
var args = new LandGolfBallInCupEventArgs
|
||||
{
|
||||
Player = player,
|
||||
Data = data,
|
||||
PlayerIndex = playerIndex,
|
||||
TileX = tileX,
|
||||
TileY = tileY,
|
||||
Hits = hits,
|
||||
ProjectileType = projectileType
|
||||
};
|
||||
LandGolfBallInCup.Invoke(null, args);
|
||||
return args.Handled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For use in a FishOutNPC event.
|
||||
/// </summary>
|
||||
|
|
@ -1997,6 +2081,40 @@ namespace TShockAPI
|
|||
return args.Handled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used when a net module is loaded
|
||||
/// </summary>
|
||||
public class ReadNetModuleEventArgs : GetDataHandledEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of net module being loaded
|
||||
/// </summary>
|
||||
public NetModuleType ModuleType { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a net module is received
|
||||
/// </summary>
|
||||
public static HandlerList<ReadNetModuleEventArgs> ReadNetModule = new HandlerList<ReadNetModuleEventArgs>();
|
||||
|
||||
private static bool OnReadNetModule(TSPlayer player, MemoryStream data, NetModuleType moduleType)
|
||||
{
|
||||
if (ReadNetModule == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var args = new ReadNetModuleEventArgs
|
||||
{
|
||||
Player = player,
|
||||
Data = data,
|
||||
ModuleType = moduleType
|
||||
};
|
||||
|
||||
ReadNetModule.Invoke(null, args);
|
||||
return args.Handled;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static bool HandlePlayerInfo(GetDataHandlerArgs args)
|
||||
|
|
@ -2254,11 +2372,10 @@ namespace TShockAPI
|
|||
|
||||
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;
|
||||
args.Player.sY = args.TPlayer.SpawnY;
|
||||
|
||||
if (((Main.tile[args.Player.sX, args.Player.sY - 1].active() && Main.tile[args.Player.sX, args.Player.sY - 1].type == 79)) && (WorldGen.StartRoomCheck(args.Player.sX, args.Player.sY - 1)))
|
||||
if (((Main.tile[args.Player.sX, args.Player.sY - 1].active() && Main.tile[args.Player.sX, args.Player.sY - 1].type == TileID.Beds)) && (WorldGen.StartRoomCheck(args.Player.sX, args.Player.sY - 1)))
|
||||
{
|
||||
args.Player.Teleport(args.Player.sX * 16, (args.Player.sY * 16) - 48);
|
||||
TShock.Log.ConsoleDebug("GetDataHandlers / HandleSpawn force teleport phase 1 {0}", args.Player.Name);
|
||||
|
|
@ -2267,7 +2384,7 @@ namespace TShockAPI
|
|||
|
||||
else if ((Main.ServerSideCharacter) && (args.Player.sX > 0) && (args.Player.sY > 0))
|
||||
{
|
||||
if (((Main.tile[args.Player.sX, args.Player.sY - 1].active() && Main.tile[args.Player.sX, args.Player.sY - 1].type == 79)) && (WorldGen.StartRoomCheck(args.Player.sX, args.Player.sY - 1)))
|
||||
if (((Main.tile[args.Player.sX, args.Player.sY - 1].active() && Main.tile[args.Player.sX, args.Player.sY - 1].type == TileID.Beds)) && (WorldGen.StartRoomCheck(args.Player.sX, args.Player.sY - 1)))
|
||||
{
|
||||
args.Player.Teleport(args.Player.sX * 16, (args.Player.sY * 16) - 48);
|
||||
TShock.Log.ConsoleDebug("GetDataHandlers / HandleSpawn force teleport phase 2 {0}", args.Player.Name);
|
||||
|
|
@ -2974,21 +3091,21 @@ namespace TShockAPI
|
|||
if (bosses.Contains(thingType) && !args.Player.HasPermission(Permissions.summonboss))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("GetDataHandlers / HandleSpawnBoss rejected boss {0} {1}", args.Player.Name, thingType);
|
||||
args.Player.SendErrorMessage("You don't have permission to summon a boss.");
|
||||
args.Player.SendErrorMessage("You do not have permission to summon a boss.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (invasions.Contains(thingType) && !args.Player.HasPermission(Permissions.startinvasion))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("GetDataHandlers / HandleSpawnBoss rejected invasion {0} {1}", args.Player.Name, thingType);
|
||||
args.Player.SendErrorMessage("You don't have permission to start an invasion.");
|
||||
args.Player.SendErrorMessage("You do not have permission to start an invasion.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pets.Contains(thingType) && !args.Player.HasPermission(Permissions.spawnpets))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("GetDataHandlers / HandleSpawnBoss rejected pet {0} {1}", args.Player.Name, thingType);
|
||||
args.Player.SendErrorMessage("You don't have permission to spawn pets.");
|
||||
args.Player.SendErrorMessage("You do not have permission to spawn pets.");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -3271,160 +3388,12 @@ namespace TShockAPI
|
|||
private static bool HandleLoadNetModule(GetDataHandlerArgs args)
|
||||
{
|
||||
short moduleId = args.Data.ReadInt16();
|
||||
if (moduleId == (int)NetModulesTypes.CreativePowers)
|
||||
|
||||
if (OnReadNetModule(args.Player, args.Data, (NetModuleType)moduleId))
|
||||
{
|
||||
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);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// As of 1.4.x.x, this is now used for more things:
|
||||
// NetCreativePowersModule
|
||||
// NetCreativePowerPermissionsModule
|
||||
// NetLiquidModule
|
||||
// NetParticlesModule
|
||||
// NetPingModule
|
||||
// NetTeleportPylonModule
|
||||
// NetTextModule
|
||||
// I (particles) have disabled the original return here, which means that we need to
|
||||
// handle this more. In the interm, this unbreaks parts of vanilla. Originally
|
||||
// we just blocked this because it was a liquid exploit.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -3622,7 +3591,7 @@ namespace TShockAPI
|
|||
if (!args.Player.HasPermission(Permissions.startdd2))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("GetDataHandlers / HandleOldOnesArmy rejected permissions {0}", args.Player.Name);
|
||||
args.Player.SendErrorMessage("You don't have permission to start the Old One's Army event.");
|
||||
args.Player.SendErrorMessage("You do not have permission to start the Old One's Army event.");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -3716,6 +3685,46 @@ namespace TShockAPI
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HandleEmoji(GetDataHandlerArgs args)
|
||||
{
|
||||
byte playerIndex = args.Data.ReadInt8();
|
||||
byte emojiID = args.Data.ReadInt8();
|
||||
|
||||
if (OnEmoji(args.Player, args.Data, playerIndex, emojiID))
|
||||
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;
|
||||
}
|
||||
|
||||
private static bool HandleLandGolfBallInCup(GetDataHandlerArgs args)
|
||||
{
|
||||
byte playerIndex = args.Data.ReadInt8();
|
||||
ushort tileX = args.Data.ReadUInt16();
|
||||
ushort tileY = args.Data.ReadUInt16();
|
||||
ushort hits = args.Data.ReadUInt16();
|
||||
ushort projectileType = args.Data.ReadUInt16();
|
||||
|
||||
if (OnLandGolfBallInCup(args.Player, args.Data, playerIndex, tileX, tileY, hits, projectileType))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HandleSyncTilePicking(GetDataHandlerArgs args)
|
||||
{
|
||||
|
|
@ -3771,6 +3780,13 @@ namespace TShockAPI
|
|||
return false;
|
||||
}
|
||||
|
||||
private static bool HandleSyncCavernMonsterType(GetDataHandlerArgs args)
|
||||
{
|
||||
args.Player.Kick("Exploit attempt detected!");
|
||||
TShock.Log.ConsoleDebug($"HandleSyncCavernMonsterType: Player is trying to modify NPC cavernMonsterType; this is a crafted packet! - From {args.Player.Name}");
|
||||
return true;
|
||||
}
|
||||
|
||||
public enum EditAction
|
||||
{
|
||||
KillTile = 0,
|
||||
|
|
@ -3952,7 +3968,7 @@ namespace TShockAPI
|
|||
public bool Killed { get; internal set; }
|
||||
}
|
||||
|
||||
public enum NetModulesTypes
|
||||
public enum NetModuleType
|
||||
{
|
||||
Liquid,
|
||||
Text,
|
||||
|
|
|
|||
32
TShockAPI/Handlers/EmojiHandler.cs
Normal file
32
TShockAPI/Handlers/EmojiHandler.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using static TShockAPI.GetDataHandlers;
|
||||
|
||||
namespace TShockAPI.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles emoji packets and checks for validity and permissions
|
||||
/// </summary>
|
||||
public class EmojiHandler : IPacketHandler<EmojiEventArgs>
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked when an emoji is sent in chat. Rejects the emoji packet if the player is spoofing IDs or does not have emoji permissions
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
public void OnReceive(object sender, EmojiEventArgs args)
|
||||
{
|
||||
if (args.PlayerIndex != args.Player.Index)
|
||||
{
|
||||
TShock.Log.ConsoleError($"EmojiHandler: Emoji packet rejected for ID spoofing. Expected {args.Player.Index}, received {args.PlayerIndex} from {args.Player.Name}.");
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.Player.HasPermission(Permissions.sendemoji))
|
||||
{
|
||||
args.Player.SendErrorMessage("You do not have permission to send emotes!");
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
TShockAPI/Handlers/IPacketHandler.cs
Normal file
16
TShockAPI/Handlers/IPacketHandler.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
namespace TShockAPI.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a packet handler that receives a packet from a GetDataHandler
|
||||
/// </summary>
|
||||
/// <typeparam name="TEventArgs"></typeparam>
|
||||
public interface IPacketHandler<TEventArgs> where TEventArgs : GetDataHandledEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked when the packet is received
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
void OnReceive(object sender, TEventArgs args);
|
||||
}
|
||||
}
|
||||
116
TShockAPI/Handlers/LandGolfBallInCupHandler.cs
Normal file
116
TShockAPI/Handlers/LandGolfBallInCupHandler.cs
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Terraria;
|
||||
using Terraria.ID;
|
||||
using static TShockAPI.GetDataHandlers;
|
||||
|
||||
namespace TShockAPI.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles client side exploits of LandGolfBallInCup packet.
|
||||
/// </summary>
|
||||
public class LandGolfBallInCupHandler : IPacketHandler<LandGolfBallInCupEventArgs>
|
||||
{
|
||||
/// <summary>
|
||||
/// List of golf ball projectile IDs.
|
||||
/// </summary>
|
||||
public static readonly List<int> GolfBallProjectileIDs = new List<int>()
|
||||
{
|
||||
ProjectileID.DirtGolfBall,
|
||||
ProjectileID.GolfBallDyedBlack,
|
||||
ProjectileID.GolfBallDyedBlue,
|
||||
ProjectileID.GolfBallDyedBrown,
|
||||
ProjectileID.GolfBallDyedCyan,
|
||||
ProjectileID.GolfBallDyedGreen,
|
||||
ProjectileID.GolfBallDyedLimeGreen,
|
||||
ProjectileID.GolfBallDyedOrange,
|
||||
ProjectileID.GolfBallDyedPink,
|
||||
ProjectileID.GolfBallDyedPurple,
|
||||
ProjectileID.GolfBallDyedRed,
|
||||
ProjectileID.GolfBallDyedSkyBlue,
|
||||
ProjectileID.GolfBallDyedTeal,
|
||||
ProjectileID.GolfBallDyedViolet,
|
||||
ProjectileID.GolfBallDyedYellow
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// List of golf club item IDs
|
||||
/// </summary>
|
||||
public static readonly List<int> GolfClubItemIDs = new List<int>()
|
||||
{
|
||||
ItemID.GolfClubChlorophyteDriver,
|
||||
ItemID.GolfClubDiamondWedge,
|
||||
ItemID.GolfClubShroomitePutter,
|
||||
ItemID.Fake_BambooChest,
|
||||
ItemID.GolfClubTitaniumIron,
|
||||
ItemID.GolfClubGoldWedge,
|
||||
ItemID.GolfClubLeadPutter,
|
||||
ItemID.GolfClubMythrilIron,
|
||||
ItemID.GolfClubWoodDriver,
|
||||
ItemID.GolfClubBronzeWedge,
|
||||
ItemID.GolfClubRustyPutter,
|
||||
ItemID.GolfClubStoneIron,
|
||||
ItemID.GolfClubPearlwoodDriver,
|
||||
ItemID.GolfClubIron,
|
||||
ItemID.GolfClubDriver,
|
||||
ItemID.GolfClubWedge,
|
||||
ItemID.GolfClubPutter
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a player lands a golf ball in a cup.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
public void OnReceive(object sender, LandGolfBallInCupEventArgs args)
|
||||
{
|
||||
if (args.PlayerIndex != args.Player.Index)
|
||||
{
|
||||
TShock.Log.ConsoleDebug($"LandGolfBallInCupHandler: Packet rejected for ID spoofing. Expected {args.PlayerIndex} , received {args.PlayerIndex} from {args.Player.Name}.");
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.TileX > Main.maxTilesX || args.TileX < 0
|
||||
|| args.TileY > Main.maxTilesY || args.TileY < 0)
|
||||
{
|
||||
TShock.Log.ConsoleDebug($"LandGolfBallInCupHandler: X and Y position is out of world bounds! - From {args.Player.Name}");
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Main.tile[args.TileX, args.TileY].active() && Main.tile[args.TileX, args.TileY].type != TileID.GolfHole)
|
||||
{
|
||||
TShock.Log.ConsoleDebug($"LandGolfBallInCupHandler: Tile at packet position X:{args.TileX} Y:{args.TileY} is not a golf hole! - From {args.Player.Name}");
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GolfBallProjectileIDs.Contains(args.ProjectileType))
|
||||
{
|
||||
TShock.Log.ConsoleDebug($"LandGolfBallInCupHandler: Invalid golf ball projectile ID {args.ProjectileType}! - From {args.Player.Name}");
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var usedGolfBall = args.Player.RecentlyCreatedProjectiles.Any(e => GolfBallProjectileIDs.Contains(e.Type));
|
||||
var usedGolfClub = args.Player.RecentlyCreatedProjectiles.Any(e => e.Type == ProjectileID.GolfClubHelper);
|
||||
if (!usedGolfClub && !usedGolfBall)
|
||||
{
|
||||
TShock.Log.ConsoleDebug($"GolfPacketHandler: Player did not have create a golf club projectile the last 5 seconds! - From {args.Player.Name}");
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GolfClubItemIDs.Contains(args.Player.SelectedItem.type))
|
||||
{
|
||||
TShock.Log.ConsoleDebug($"LandGolfBallInCupHandler: Item selected is not a golf club! - From {args.Player.Name}");
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
TShockAPI/Handlers/NetModules/AmbienceHandler.cs
Normal file
28
TShockAPI/Handlers/NetModules/AmbienceHandler.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using System.IO;
|
||||
|
||||
namespace TShockAPI.Handlers.NetModules
|
||||
{
|
||||
/// <summary>
|
||||
/// Rejects ambience new modules from clients
|
||||
/// </summary>
|
||||
public class AmbienceHandler : INetModuleHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// No deserialization needed. This should never be received by the server
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public void Deserialize(MemoryStream data)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This should never be received by the server
|
||||
/// </summary>
|
||||
/// <param name="player"></param>
|
||||
/// <param name="rejectPacket"></param>
|
||||
public void HandlePacket(TSPlayer player, out bool rejectPacket)
|
||||
{
|
||||
rejectPacket = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
TShockAPI/Handlers/NetModules/BestiaryHandler.cs
Normal file
28
TShockAPI/Handlers/NetModules/BestiaryHandler.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using System.IO;
|
||||
|
||||
namespace TShockAPI.Handlers.NetModules
|
||||
{
|
||||
/// <summary>
|
||||
/// Rejects client->server bestiary net modules as the client should never send this to the server
|
||||
/// </summary>
|
||||
public class BestiaryHandler : INetModuleHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// No deserialization needed. This should never be received by the server
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public void Deserialize(MemoryStream data)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This should never be received by the server
|
||||
/// </summary>
|
||||
/// <param name="player"></param>
|
||||
/// <param name="rejectPacket"></param>
|
||||
public void HandlePacket(TSPlayer player, out bool rejectPacket)
|
||||
{
|
||||
rejectPacket = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
113
TShockAPI/Handlers/NetModules/CreativePowerHandler.cs
Normal file
113
TShockAPI/Handlers/NetModules/CreativePowerHandler.cs
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Streams;
|
||||
using static TShockAPI.GetDataHandlers;
|
||||
|
||||
namespace TShockAPI.Handlers.NetModules
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides handling for the Creative Power net module. Checks permissions on all creative powers
|
||||
/// </summary>
|
||||
public class CreativePowerHandler : INetModuleHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// The power type being activated
|
||||
/// </summary>
|
||||
public CreativePowerTypes PowerType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads the power type from the stream
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public void Deserialize(MemoryStream data)
|
||||
{
|
||||
PowerType = (CreativePowerTypes)data.ReadInt16();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the player has permission to use the power type
|
||||
/// </summary>
|
||||
/// <param name="player"></param>
|
||||
/// <param name="rejectPacket"></param>
|
||||
public void HandlePacket(TSPlayer player, out bool rejectPacket)
|
||||
{
|
||||
if (!HasPermission(PowerType, player))
|
||||
{
|
||||
rejectPacket = true;
|
||||
return;
|
||||
}
|
||||
|
||||
rejectPacket = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a player has permission to use a specific creative power
|
||||
/// </summary>
|
||||
/// <param name="powerType"></param>
|
||||
/// <param name="player"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasPermission(CreativePowerTypes powerType, TSPlayer player)
|
||||
{
|
||||
if (!PowerToPermissionMap.ContainsKey(powerType))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("CreativePowerHandler received permission check request for unknown creative power");
|
||||
return false;
|
||||
}
|
||||
|
||||
string permission = PowerToPermissionMap[powerType];
|
||||
|
||||
if (!player.HasPermission(permission))
|
||||
{
|
||||
player.SendErrorMessage("You do not have permission to {0}.", PermissionToDescriptionMap[permission]);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Maps creative powers to permission nodes
|
||||
/// </summary>
|
||||
public static Dictionary<CreativePowerTypes, string> PowerToPermissionMap = new Dictionary<CreativePowerTypes, string>
|
||||
{
|
||||
{ CreativePowerTypes.FreezeTime, Permissions.journey_timefreeze },
|
||||
{ CreativePowerTypes.SetDawn, Permissions.journey_timeset },
|
||||
{ CreativePowerTypes.SetNoon, Permissions.journey_timeset },
|
||||
{ CreativePowerTypes.SetDusk, Permissions.journey_timeset },
|
||||
{ CreativePowerTypes.SetMidnight, Permissions.journey_timeset },
|
||||
{ CreativePowerTypes.Godmode, Permissions.journey_godmode },
|
||||
{ CreativePowerTypes.WindStrength, Permissions.journey_windstrength },
|
||||
{ CreativePowerTypes.RainStrength, Permissions.journey_rainstrength },
|
||||
{ CreativePowerTypes.TimeSpeed, Permissions.journey_timespeed },
|
||||
{ CreativePowerTypes.RainFreeze, Permissions.journey_rainfreeze },
|
||||
{ CreativePowerTypes.WindFreeze, Permissions.journey_windfreeze },
|
||||
{ CreativePowerTypes.IncreasePlacementRange, Permissions.journey_placementrange },
|
||||
{ CreativePowerTypes.WorldDifficulty, Permissions.journey_setdifficulty },
|
||||
{ CreativePowerTypes.BiomeSpreadFreeze, Permissions.journey_biomespreadfreeze },
|
||||
{ CreativePowerTypes.SetSpawnRate, Permissions.journey_setspawnrate },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Maps journey mode permission nodes to descriptions of what the permission allows
|
||||
/// </summary>
|
||||
public static Dictionary<string, string> PermissionToDescriptionMap = new Dictionary<string, string>
|
||||
{
|
||||
{ Permissions.journey_timefreeze, "freeze the time of the server" },
|
||||
{ Permissions.journey_timeset, "modify the time of the server" },
|
||||
{ Permissions.journey_timeset, "modify the time of the server" },
|
||||
{ Permissions.journey_timeset, "modify the time of the server" },
|
||||
{ Permissions.journey_timeset, "modify the time of the server" },
|
||||
{ Permissions.journey_godmode, "toggle godmode" },
|
||||
{ Permissions.journey_windstrength, "modify the wind strength of the server" },
|
||||
{ Permissions.journey_rainstrength, "modify the rain strength of the server" },
|
||||
{ Permissions.journey_timespeed, "modify the time speed of the server" },
|
||||
{ Permissions.journey_rainfreeze, "freeze the rain strength of the server" },
|
||||
{ Permissions.journey_windfreeze, "freeze the wind strength of the server" },
|
||||
{ Permissions.journey_placementrange, "modify the tile placement range of your character" },
|
||||
{ Permissions.journey_setdifficulty, "modify the world difficulty of the server" },
|
||||
{ Permissions.journey_biomespreadfreeze, "freeze the biome spread of the server" },
|
||||
{ Permissions.journey_setspawnrate, "modify the NPC spawn rate of the server" },
|
||||
};
|
||||
}
|
||||
}
|
||||
90
TShockAPI/Handlers/NetModules/CreativeUnlocksHandler.cs
Normal file
90
TShockAPI/Handlers/NetModules/CreativeUnlocksHandler.cs
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
using System.IO;
|
||||
using System.IO.Streams;
|
||||
using Terraria;
|
||||
using Terraria.GameContent.NetModules;
|
||||
using Terraria.Net;
|
||||
|
||||
namespace TShockAPI.Handlers.NetModules
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles creative unlock requests
|
||||
/// </summary>
|
||||
public class CreativeUnlocksHandler : INetModuleHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// An unknown field. If this does not have a value of '0' the packet should be rejected.
|
||||
/// </summary>
|
||||
public byte UnknownField { get; set; }
|
||||
/// <summary>
|
||||
/// ID of the item being sacrificed
|
||||
/// </summary>
|
||||
public ushort ItemId { get; set; }
|
||||
/// <summary>
|
||||
/// Stack size of the item being sacrificed
|
||||
/// </summary>
|
||||
public ushort Amount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads the unlock data from the stream
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public void Deserialize(MemoryStream data)
|
||||
{
|
||||
// For whatever reason Terraria writes '0' to the stream at the beginning of this packet.
|
||||
// If this value is not 0 then its been crafted by a non-vanilla client.
|
||||
// We don't actually know why the 0 is written, so we're just going to call this UnknownField for now
|
||||
UnknownField = data.ReadInt8();
|
||||
if (UnknownField == 0)
|
||||
{
|
||||
ItemId = data.ReadUInt16();
|
||||
Amount = data.ReadUInt16();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the unlock is valid and the player has permission to perform the unlock.
|
||||
/// Syncs unlock status if the packet is accepted
|
||||
/// </summary>
|
||||
/// <param name="player"></param>
|
||||
/// <param name="rejectPacket"></param>
|
||||
public void HandlePacket(TSPlayer player, out bool rejectPacket)
|
||||
{
|
||||
if (!Main.GameModeInfo.IsJourneyMode)
|
||||
{
|
||||
TShock.Log.ConsoleDebug(
|
||||
"NetModuleHandler received attempt to unlock sacrifice while not in journey mode from",
|
||||
player.Name
|
||||
);
|
||||
|
||||
rejectPacket = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (UnknownField != 0)
|
||||
{
|
||||
TShock.Log.ConsoleDebug(
|
||||
"CreativeUnlocksHandler received non-vanilla unlock request. Random field value: {0} but should be 0 from {1}",
|
||||
UnknownField,
|
||||
player.Name
|
||||
);
|
||||
|
||||
rejectPacket = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player.HasPermission(Permissions.journey_contributeresearch))
|
||||
{
|
||||
player.SendErrorMessage("You do not have permission to contribute research.");
|
||||
rejectPacket = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var totalSacrificed = TShock.ResearchDatastore.SacrificeItem(ItemId, Amount, player);
|
||||
|
||||
var response = NetCreativeUnlocksModule.SerializeItemSacrifice(ItemId, totalSacrificed);
|
||||
NetManager.Instance.Broadcast(response);
|
||||
|
||||
rejectPacket = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
TShockAPI/Handlers/NetModules/INetModuleHandler.cs
Normal file
23
TShockAPI/Handlers/NetModules/INetModuleHandler.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
using System.IO;
|
||||
|
||||
namespace TShockAPI.Handlers.NetModules
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a handler for a net module
|
||||
/// </summary>
|
||||
public interface INetModuleHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads the net module's data from the given stream
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
void Deserialize(MemoryStream data);
|
||||
|
||||
/// <summary>
|
||||
/// Provides handling for the packet and determines if it should be accepted or rejected
|
||||
/// </summary>
|
||||
/// <param name="player"></param>
|
||||
/// <param name="rejectPacket"></param>
|
||||
void HandlePacket(TSPlayer player, out bool rejectPacket);
|
||||
}
|
||||
}
|
||||
29
TShockAPI/Handlers/NetModules/LiquidHandler.cs
Normal file
29
TShockAPI/Handlers/NetModules/LiquidHandler.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using System.IO;
|
||||
|
||||
namespace TShockAPI.Handlers.NetModules
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles the NetLiquidModule. Rejects all incoming net liquid requests, as clients should never send them
|
||||
/// </summary>
|
||||
public class LiquidHandler : INetModuleHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Does nothing. We should not deserialize this data
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public void Deserialize(MemoryStream data)
|
||||
{
|
||||
// No need to deserialize
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rejects the packet. Clients should not send this to us
|
||||
/// </summary>
|
||||
/// <param name="player"></param>
|
||||
/// <param name="rejectPacket"></param>
|
||||
public void HandlePacket(TSPlayer player, out bool rejectPacket)
|
||||
{
|
||||
rejectPacket = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
TShockAPI/Handlers/NetModules/NetModulePacketHandler.cs
Normal file
54
TShockAPI/Handlers/NetModules/NetModulePacketHandler.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Terraria;
|
||||
using static TShockAPI.GetDataHandlers;
|
||||
|
||||
namespace TShockAPI.Handlers.NetModules
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles packet 82 - Load Net Module packets
|
||||
/// </summary>
|
||||
public class NetModulePacketHandler : IPacketHandler<ReadNetModuleEventArgs>
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps net module types to handlers for the net module type. Add to or edit this dictionary to customise handling
|
||||
/// </summary>
|
||||
public static Dictionary<NetModuleType, Type> NetModulesToHandlersMap = new Dictionary<NetModuleType, Type>
|
||||
{
|
||||
{ NetModuleType.CreativePowers, typeof(CreativePowerHandler) },
|
||||
{ NetModuleType.CreativeUnlocksPlayerReport, typeof(CreativeUnlocksHandler) },
|
||||
{ NetModuleType.TeleportPylon, typeof(PylonHandler) },
|
||||
{ NetModuleType.Liquid, typeof(LiquidHandler) },
|
||||
{ NetModuleType.Bestiary, typeof(BestiaryHandler) },
|
||||
{ NetModuleType.Ambience, typeof(AmbienceHandler) }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a load net module packet is received. This method picks a <see cref="INetModuleHandler"/> based on the
|
||||
/// net module type being loaded, then forwards the data to the chosen handler to process
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
public void OnReceive(object sender, ReadNetModuleEventArgs args)
|
||||
{
|
||||
INetModuleHandler handler;
|
||||
|
||||
if (NetModulesToHandlersMap.ContainsKey(args.ModuleType))
|
||||
{
|
||||
handler = (INetModuleHandler)Activator.CreateInstance(NetModulesToHandlersMap[args.ModuleType]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't have handlers for NetModuleType.Ping and NetModuleType.Particles.
|
||||
// These net modules are fairly innocuous and can be processed normally by the game
|
||||
args.Handled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
handler.Deserialize(args.Data);
|
||||
handler.HandlePacket(args.Player, out bool rejectPacket);
|
||||
|
||||
args.Handled = rejectPacket;
|
||||
}
|
||||
}
|
||||
}
|
||||
62
TShockAPI/Handlers/NetModules/PylonHandler.cs
Normal file
62
TShockAPI/Handlers/NetModules/PylonHandler.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
using System.IO;
|
||||
using System.IO.Streams;
|
||||
using Terraria.GameContent;
|
||||
using static Terraria.GameContent.NetModules.NetTeleportPylonModule;
|
||||
|
||||
namespace TShockAPI.Handlers.NetModules
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles a pylon net module
|
||||
/// </summary>
|
||||
public class PylonHandler : INetModuleHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Event occuring
|
||||
/// </summary>
|
||||
public SubPacketType PylonEventType { get; set; }
|
||||
/// <summary>
|
||||
/// Tile X coordinate of the pylon
|
||||
/// </summary>
|
||||
public short TileX { get; set; }
|
||||
/// <summary>
|
||||
/// Tile Y coordinate of the pylon
|
||||
/// </summary>
|
||||
public short TileY { get; set; }
|
||||
/// <summary>
|
||||
/// Type of Pylon
|
||||
/// </summary>
|
||||
public TeleportPylonType PylonType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads the pylon data from the net module
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public void Deserialize(MemoryStream data)
|
||||
{
|
||||
PylonEventType = (SubPacketType)data.ReadInt8();
|
||||
TileX = data.ReadInt16();
|
||||
TileY = data.ReadInt16();
|
||||
PylonType = (TeleportPylonType)data.ReadInt8();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rejects a pylon teleport request if the player does not have permission
|
||||
/// </summary>
|
||||
/// <param name="player"></param>
|
||||
/// <param name="rejectPacket"></param>
|
||||
public void HandlePacket(TSPlayer player, out bool rejectPacket)
|
||||
{
|
||||
if (PylonEventType == SubPacketType.PlayerRequestsTeleport)
|
||||
{
|
||||
if (!player.HasPermission(Permissions.pylon))
|
||||
{
|
||||
rejectPacket = true;
|
||||
player.SendErrorMessage("You do not have permission to teleport with pylons.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rejectPacket = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,12 +14,12 @@ namespace TShockAPI.Handlers
|
|||
/// <summary>
|
||||
/// Provides processors for handling Tile Square packets
|
||||
/// </summary>
|
||||
public class SendTileSquareHandler
|
||||
public class SendTileSquareHandler : IPacketHandler<GetDataHandlers.SendTileSquareEventArgs>
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps grass-type blocks to flowers that can be grown on them with flower boots
|
||||
/// </summary>
|
||||
Dictionary<ushort, List<ushort>> _grassToPlantMap = new Dictionary<ushort, List<ushort>>
|
||||
public static Dictionary<ushort, List<ushort>> GrassToPlantMap = new Dictionary<ushort, List<ushort>>
|
||||
{
|
||||
{ TileID.Grass, new List<ushort> { TileID.Plants, TileID.Plants2 } },
|
||||
{ TileID.HallowedGrass, new List<ushort> { TileID.HallowedPlants, TileID.HallowedPlants2 } },
|
||||
|
|
@ -29,7 +29,7 @@ namespace TShockAPI.Handlers
|
|||
/// <summary>
|
||||
/// Item IDs that can spawn flowers while you walk
|
||||
/// </summary>
|
||||
List<int> _flowerBootItems = new List<int>
|
||||
public static List<int> FlowerBootItems = new List<int>
|
||||
{
|
||||
ItemID.FlowerBoots,
|
||||
ItemID.FairyBoots
|
||||
|
|
@ -40,7 +40,7 @@ namespace TShockAPI.Handlers
|
|||
/// Note: <see cref="Terraria.ID.TileEntityID"/> is empty at the time of writing, but entities are dynamically assigned their ID at initialize time
|
||||
/// which is why we can use the _myEntityId field on each entity type
|
||||
/// </summary>
|
||||
Dictionary<int, int> _tileEntityIdToTileIdMap = new Dictionary<int, int>
|
||||
public static Dictionary<int, int> TileEntityIdToTileIdMap = new Dictionary<int, int>
|
||||
{
|
||||
{ TileID.TargetDummy, TETrainingDummy._myEntityID },
|
||||
{ TileID.ItemFrame, TEItemFrame._myEntityID },
|
||||
|
|
@ -57,7 +57,7 @@ namespace TShockAPI.Handlers
|
|||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
public void OnReceiveSendTileSquare(object sender, GetDataHandlers.SendTileSquareEventArgs args)
|
||||
public void OnReceive(object sender, GetDataHandlers.SendTileSquareEventArgs args)
|
||||
{
|
||||
// By default, we'll handle everything
|
||||
args.Handled = true;
|
||||
|
|
@ -191,14 +191,20 @@ namespace TShockAPI.Handlers
|
|||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected from no permission for tile object from {0}", args.Player.Name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TShock.TileBans.TileIsBanned((short)tileType))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected for banned tile");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update all tiles in the tile object. These will be sent back to the player later
|
||||
UpdateMultipleServerTileStates(realX, realY, width, height, newTiles);
|
||||
|
||||
// Tile entities have special placements that we should let the game deal with
|
||||
if (_tileEntityIdToTileIdMap.ContainsKey(tileType))
|
||||
if (TileEntityIdToTileIdMap.ContainsKey(tileType))
|
||||
{
|
||||
TileEntity.PlaceEntityNet(realX, realY, _tileEntityIdToTileIdMap[tileType]);
|
||||
TileEntity.PlaceEntityNet(realX, realY, TileEntityIdToTileIdMap[tileType]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -214,7 +220,7 @@ namespace TShockAPI.Handlers
|
|||
{
|
||||
// Some boots allow growing flowers on grass. This process sends a 1x1 tile square to grow the flowers
|
||||
// The square size must be 1 and the player must have an accessory that allows growing flowers in order for this square to be valid
|
||||
if (squareSize == 1 && args.Player.Accessories.Any(a => a != null && _flowerBootItems.Contains(a.type)))
|
||||
if (squareSize == 1 && args.Player.Accessories.Any(a => a != null && FlowerBootItems.Contains(a.type)))
|
||||
{
|
||||
ProcessFlowerBoots(realX, realY, newTile, args);
|
||||
return;
|
||||
|
|
@ -254,7 +260,7 @@ namespace TShockAPI.Handlers
|
|||
}
|
||||
|
||||
ITile tile = Main.tile[realX, realY + 1];
|
||||
if (!_grassToPlantMap.TryGetValue(tile.type, out List<ushort> plantTiles) && !plantTiles.Contains(newTile.Type))
|
||||
if (!GrassToPlantMap.TryGetValue(tile.type, out List<ushort> plantTiles) && !plantTiles.Contains(newTile.Type))
|
||||
{
|
||||
// If the tile below the tile square isn't a valid plant tile (eg grass) then we don't update the server tile state
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -267,6 +267,9 @@ namespace TShockAPI
|
|||
|
||||
[Description("User can use wormhole potions.")]
|
||||
public static readonly string wormhole = "tshock.tp.wormhole";
|
||||
|
||||
[Description("User can use pylons to teleport")]
|
||||
public static readonly string pylon = "tshock.tp.pylon";
|
||||
#endregion
|
||||
|
||||
#region tshock.world nodes
|
||||
|
|
@ -409,6 +412,9 @@ namespace TShockAPI
|
|||
|
||||
[Description("User can use Creative UI to set the NPC spawn rate of the world.")]
|
||||
public static readonly string journey_setspawnrate = "tshock.journey.setspawnrate";
|
||||
|
||||
[Description("User can contribute research by sacrificing items")]
|
||||
public static readonly string journey_contributeresearch = "tshock.journey.research";
|
||||
#endregion
|
||||
|
||||
#region Non-grouped
|
||||
|
|
@ -468,6 +474,9 @@ namespace TShockAPI
|
|||
|
||||
[Description("Player can resync themselves with server state.")]
|
||||
public static readonly string synclocalarea = "tshock.synclocalarea";
|
||||
|
||||
[Description("Player can send emotes.")]
|
||||
public static readonly string sendemoji = "tshock.sendemoji";
|
||||
#endregion
|
||||
/// <summary>
|
||||
/// Lists all commands associated with a given permission
|
||||
|
|
|
|||
|
|
@ -664,13 +664,13 @@ namespace TShockAPI
|
|||
switch (failure)
|
||||
{
|
||||
case BuildPermissionFailPoint.GeneralBuild:
|
||||
SendErrorMessage("You lack permission to build on this server.");
|
||||
SendErrorMessage("You do not have permission to build on this server.");
|
||||
break;
|
||||
case BuildPermissionFailPoint.SpawnProtect:
|
||||
SendErrorMessage("You lack permission to build in the spawn point.");
|
||||
SendErrorMessage("You do not have permission to build in the spawn point.");
|
||||
break;
|
||||
case BuildPermissionFailPoint.Regions:
|
||||
SendErrorMessage("You lack permission to build in this region.");
|
||||
SendErrorMessage("You do not have permission to build in this region.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,17 @@
|
|||
<Compile Include="DB\ResearchDatastore.cs" />
|
||||
<Compile Include="DB\TileManager.cs" />
|
||||
<Compile Include="Extensions\ExceptionExt.cs" />
|
||||
<Compile Include="Handlers\IPacketHandler.cs" />
|
||||
<Compile Include="Handlers\NetModules\AmbienceHandler.cs" />
|
||||
<Compile Include="Handlers\NetModules\BestiaryHandler.cs" />
|
||||
<Compile Include="Handlers\NetModules\CreativePowerHandler.cs" />
|
||||
<Compile Include="Handlers\NetModules\CreativeUnlocksHandler.cs" />
|
||||
<Compile Include="Handlers\NetModules\INetModuleHandler.cs" />
|
||||
<Compile Include="Handlers\NetModules\LiquidHandler.cs" />
|
||||
<Compile Include="Handlers\NetModules\NetModulePacketHandler.cs" />
|
||||
<Compile Include="Handlers\NetModules\PylonHandler.cs" />
|
||||
<Compile Include="Handlers\EmojiHandler.cs" />
|
||||
<Compile Include="Handlers\LandGolfBallInCupHandler.cs" />
|
||||
<Compile Include="Handlers\SendTileSquareHandler.cs" />
|
||||
<Compile Include="Hooks\AccountHooks.cs" />
|
||||
<Compile Include="Hooks\GeneralHooks.cs" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue