From 48d610d33f8df30b5da9c1eb797b23f6fd5a44ea Mon Sep 17 00:00:00 2001 From: Chris <2648373+QuiCM@users.noreply.github.com> Date: Tue, 2 Jun 2020 12:59:14 +0930 Subject: [PATCH] Move netmodule handling into handlers namespace Add handler for teleport pylons, and permission for teleport pylons --- TShockAPI/Bouncer.cs | 4 + TShockAPI/GetDataHandlers.cs | 188 ++++-------------- .../NetModules/CreativePowerHandler.cs | 113 +++++++++++ .../NetModules/CreativeUnlocksHandler.cs | 75 +++++++ .../Handlers/NetModules/INetModuleHandler.cs | 23 +++ .../NetModules/NetModulePacketHandler.cs | 75 +++++++ TShockAPI/Handlers/NetModules/PylonHandler.cs | 62 ++++++ TShockAPI/Permissions.cs | 6 + TShockAPI/TShockAPI.csproj | 7 +- 9 files changed, 401 insertions(+), 152 deletions(-) create mode 100644 TShockAPI/Handlers/NetModules/CreativePowerHandler.cs create mode 100644 TShockAPI/Handlers/NetModules/CreativeUnlocksHandler.cs create mode 100644 TShockAPI/Handlers/NetModules/INetModuleHandler.cs create mode 100644 TShockAPI/Handlers/NetModules/NetModulePacketHandler.cs create mode 100644 TShockAPI/Handlers/NetModules/PylonHandler.cs diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index fda85267..fa928eb4 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -37,6 +37,7 @@ namespace TShockAPI internal sealed class Bouncer { internal Handlers.SendTileSquareHandler STSHandler { get; set; } + internal Handlers.NetModules.NetModulePacketHandler NetModuleHandler { get; set; } /// Constructor call initializes Bouncer and related functionality. /// A new Bouncer. @@ -45,6 +46,9 @@ namespace TShockAPI STSHandler = new Handlers.SendTileSquareHandler(); GetDataHandlers.SendTileSquare += STSHandler.OnReceive; + NetModuleHandler = new Handlers.NetModules.NetModulePacketHandler(); + GetDataHandlers.LoadNetModule += NetModuleHandler.OnReceive; + // Setup hooks GetDataHandlers.GetSection += OnGetSection; GetDataHandlers.PlayerUpdate += OnPlayerUpdate; diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 65fe622f..52394f38 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -1951,6 +1951,40 @@ namespace TShockAPI return args.Handled; } + /// + /// Used when a net module is loaded + /// + public class LoadNetModuleEventArgs : GetDataHandledEventArgs + { + /// + /// The type of net module being loaded + /// + public NetModulesTypes ModuleType { get; set; } + } + + /// + /// Called when a net module is loaded + /// + public static HandlerList LoadNetModule = new HandlerList(); + + private static bool OnLoadNetModule(TSPlayer player, MemoryStream data, NetModulesTypes moduleType) + { + if (LoadNetModule == null) + { + return false; + } + + var args = new LoadNetModuleEventArgs + { + Player = player, + Data = data, + ModuleType = moduleType + }; + + LoadNetModule.Invoke(null, args); + return args.Handled; + } + #endregion private static bool HandlePlayerInfo(GetDataHandlerArgs args) @@ -3225,160 +3259,12 @@ namespace TShockAPI private static bool HandleLoadNetModule(GetDataHandlerArgs args) { short moduleId = args.Data.ReadInt16(); - if (moduleId == (int)NetModulesTypes.CreativePowers) + + if (OnLoadNetModule(args.Player, args.Data, (NetModulesTypes)moduleId)) { - CreativePowerTypes powerId = (CreativePowerTypes)args.Data.ReadInt16(); - switch (powerId) - { - case CreativePowerTypes.FreezeTime: - { - if (!args.Player.HasPermission(Permissions.journey_timefreeze)) - { - args.Player.SendErrorMessage("You do not 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 do not 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 do not have permission to toggle godmode."); - return true; - } - break; - } - case CreativePowerTypes.WindStrength: - { - if (!args.Player.HasPermission(Permissions.journey_windstrength)) - { - args.Player.SendErrorMessage("You do not 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 do not 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 do not 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 do not 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 do not 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 do not 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 do not 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 do not 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 do not 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; } diff --git a/TShockAPI/Handlers/NetModules/CreativePowerHandler.cs b/TShockAPI/Handlers/NetModules/CreativePowerHandler.cs new file mode 100644 index 00000000..06e43107 --- /dev/null +++ b/TShockAPI/Handlers/NetModules/CreativePowerHandler.cs @@ -0,0 +1,113 @@ +using System.Collections.Generic; +using System.IO; +using System.IO.Streams; +using static TShockAPI.GetDataHandlers; + +namespace TShockAPI.Handlers.NetModules +{ + /// + /// Provides handling for the Creative Power net module. Checks permissions on all creative powers + /// + public class CreativePowerHandler : INetModuleHandler + { + /// + /// The power type being activated + /// + public CreativePowerTypes PowerType { get; set; } + + /// + /// Reads the power type from the stream + /// + /// + public void Deserialize(MemoryStream data) + { + PowerType = (CreativePowerTypes)data.ReadInt16(); + } + + /// + /// Determines if the player has permission to use the power type + /// + /// + /// + public void HandlePacket(TSPlayer player, out bool rejectPacket) + { + if (!CheckPermission(PowerType, player)) + { + rejectPacket = true; + return; + } + + rejectPacket = false; + } + + /// + /// Determines if a player has permission to use a specific creative power + /// + /// + /// + /// + public static bool CheckPermission(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; + } + + + /// + /// Maps creative powers to permission nodes + /// + public static Dictionary PowerToPermissionMap = new Dictionary + { + { 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 }, + }; + + /// + /// Maps journey mode permission nodes to descriptions of what the permission allows + /// + public static Dictionary PermissionToDescriptionMap = new Dictionary + { + { 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" }, + }; + } +} diff --git a/TShockAPI/Handlers/NetModules/CreativeUnlocksHandler.cs b/TShockAPI/Handlers/NetModules/CreativeUnlocksHandler.cs new file mode 100644 index 00000000..39f83d67 --- /dev/null +++ b/TShockAPI/Handlers/NetModules/CreativeUnlocksHandler.cs @@ -0,0 +1,75 @@ +using System.IO; +using System.IO.Streams; +using Terraria.GameContent.NetModules; +using Terraria.Net; + +namespace TShockAPI.Handlers.NetModules +{ + /// + /// Handles creative unlock requests + /// + public class CreativeUnlocksHandler : INetModuleHandler + { + /// + /// An unknown field. If this does not have a value of '0' the packet should be rejected. + /// + public byte UnknownField { get; set; } + /// + /// ID of the item being sacrificed + /// + public ushort ItemId { get; set; } + /// + /// Stack size of the item being sacrificed + /// + public ushort Amount { get; set; } + + /// + /// Reads the unlock data from the stream + /// + /// + public void Deserialize(MemoryStream data) + { + UnknownField = data.ReadInt8(); + if (UnknownField == 0) + { + ItemId = data.ReadUInt16(); + Amount = data.ReadUInt16(); + } + } + + /// + /// Determines if the unlock is valid and the player has permission to perform the unlock. + /// Syncs unlock status if the packet is accepted + /// + /// + /// + public void HandlePacket(TSPlayer player, out bool rejectPacket) + { + if (UnknownField != 0) + { + TShock.Log.ConsoleDebug( + "CreativeUnlocksNetModuleHandler received non-vanilla unlock request. Random field value: {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; + } + } +} diff --git a/TShockAPI/Handlers/NetModules/INetModuleHandler.cs b/TShockAPI/Handlers/NetModules/INetModuleHandler.cs new file mode 100644 index 00000000..459d22f4 --- /dev/null +++ b/TShockAPI/Handlers/NetModules/INetModuleHandler.cs @@ -0,0 +1,23 @@ +using System.IO; + +namespace TShockAPI.Handlers.NetModules +{ + /// + /// Describes a handler for a net module + /// + public interface INetModuleHandler + { + /// + /// Reads the net module's data from the given stream + /// + /// + void Deserialize(MemoryStream data); + + /// + /// Provides handling for the packet and determines if it should be accepted or rejected + /// + /// + /// + void HandlePacket(TSPlayer player, out bool rejectPacket); + } +} diff --git a/TShockAPI/Handlers/NetModules/NetModulePacketHandler.cs b/TShockAPI/Handlers/NetModules/NetModulePacketHandler.cs new file mode 100644 index 00000000..f4d20bb4 --- /dev/null +++ b/TShockAPI/Handlers/NetModules/NetModulePacketHandler.cs @@ -0,0 +1,75 @@ +using System; +using Terraria; +using static TShockAPI.GetDataHandlers; + +namespace TShockAPI.Handlers.NetModules +{ + /// + /// Handles packet 82 - Load Net Module packets + /// + public class NetModulePacketHandler : IPacketHandler + { + /// + /// Invoked when a load net module packet is received. This method picks a based on the + /// net module type being loaded, then forwards the data to the chosen handler to process + /// + /// + /// + public void OnReceive(object sender, LoadNetModuleEventArgs args) + { + INetModuleHandler handler; + + switch (args.ModuleType) + { + case NetModulesTypes.CreativePowers: + { + handler = new CreativePowerHandler(); + break; + } + + case NetModulesTypes.CreativeUnlocksPlayerReport: + { + if (!Main.GameModeInfo.IsJourneyMode) + { + TShock.Log.ConsoleDebug( + "NetModuleHandler received attempt to unlock sacrifice while not in journey mode from", + args.Player.Name + ); + + args.Handled = true; + return; + } + + handler = new CreativeUnlocksHandler(); + break; + } + case NetModulesTypes.TeleportPylon: + { + handler = new PylonHandler(); + break; + } + default: + { + // 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. + args.Handled = false; + return; + } + } + + handler.Deserialize(args.Data); + handler.HandlePacket(args.Player, out bool rejectPacket); + + args.Handled = rejectPacket; + } + } +} diff --git a/TShockAPI/Handlers/NetModules/PylonHandler.cs b/TShockAPI/Handlers/NetModules/PylonHandler.cs new file mode 100644 index 00000000..75ca0c1d --- /dev/null +++ b/TShockAPI/Handlers/NetModules/PylonHandler.cs @@ -0,0 +1,62 @@ +using System.IO; +using System.IO.Streams; +using Terraria.GameContent; +using static Terraria.GameContent.NetModules.NetTeleportPylonModule; + +namespace TShockAPI.Handlers.NetModules +{ + /// + /// Handles a pylon net module + /// + public class PylonHandler : INetModuleHandler + { + /// + /// Event occuring + /// + public SubPacketType PylonEventType { get; set; } + /// + /// Tile X coordinate of the pylon + /// + public short TileX { get; set; } + /// + /// Tile Y coordinate of the pylon + /// + public short TileY { get; set; } + /// + /// Type of Pylon + /// + public TeleportPylonType PylonType { get; set; } + + /// + /// Reads the pylon data from the net module + /// + /// + public void Deserialize(MemoryStream data) + { + PylonEventType = (SubPacketType)data.ReadInt8(); + TileX = data.ReadInt16(); + TileY = data.ReadInt16(); + PylonType = (TeleportPylonType)data.ReadInt8(); + } + + /// + /// Rejects a pylon teleport request if the player does not have permission + /// + /// + /// + 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; + } + } +} diff --git a/TShockAPI/Permissions.cs b/TShockAPI/Permissions.cs index baf21a73..3b401004 100644 --- a/TShockAPI/Permissions.cs +++ b/TShockAPI/Permissions.cs @@ -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 diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj index af383252..b129837f 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -89,6 +89,11 @@ + + + + + @@ -213,7 +218,7 @@ - +