Merge pull request #1975 from Pryaxis/more-net-modules

Move netmodule handling into handlers namespace
This commit is contained in:
Chris 2020-06-02 17:31:59 +09:30 committed by GitHub
commit d7798b937c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 488 additions and 157 deletions

View file

@ -3,7 +3,8 @@
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. 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 ## Upcoming Changes
* Your change goes here! * 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)
## TShock 4.4.0 (Pre-release 10) ## TShock 4.4.0 (Pre-release 10)
* Fix all rope coils. (@Olink) * Fix all rope coils. (@Olink)

View file

@ -37,6 +37,7 @@ namespace TShockAPI
internal sealed class Bouncer internal sealed class Bouncer
{ {
internal Handlers.SendTileSquareHandler STSHandler { get; set; } internal Handlers.SendTileSquareHandler STSHandler { get; set; }
internal Handlers.NetModules.NetModulePacketHandler NetModuleHandler { get; set; }
/// <summary>Constructor call initializes Bouncer and related functionality.</summary> /// <summary>Constructor call initializes Bouncer and related functionality.</summary>
/// <returns>A new Bouncer.</returns> /// <returns>A new Bouncer.</returns>
@ -45,6 +46,9 @@ namespace TShockAPI
STSHandler = new Handlers.SendTileSquareHandler(); STSHandler = new Handlers.SendTileSquareHandler();
GetDataHandlers.SendTileSquare += STSHandler.OnReceive; GetDataHandlers.SendTileSquare += STSHandler.OnReceive;
NetModuleHandler = new Handlers.NetModules.NetModulePacketHandler();
GetDataHandlers.ReadNetModule += NetModuleHandler.OnReceive;
// Setup hooks // Setup hooks
GetDataHandlers.GetSection += OnGetSection; GetDataHandlers.GetSection += OnGetSection;
GetDataHandlers.PlayerUpdate += OnPlayerUpdate; GetDataHandlers.PlayerUpdate += OnPlayerUpdate;

View file

@ -1951,6 +1951,40 @@ namespace TShockAPI
return args.Handled; 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 #endregion
private static bool HandlePlayerInfo(GetDataHandlerArgs args) private static bool HandlePlayerInfo(GetDataHandlerArgs args)
@ -2208,11 +2242,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))) 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.sX = args.TPlayer.SpawnX;
args.Player.sY = args.TPlayer.SpawnY; 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); 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); TShock.Log.ConsoleDebug("GetDataHandlers / HandleSpawn force teleport phase 1 {0}", args.Player.Name);
@ -2221,7 +2254,7 @@ namespace TShockAPI
else if ((Main.ServerSideCharacter) && (args.Player.sX > 0) && (args.Player.sY > 0)) 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); 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); TShock.Log.ConsoleDebug("GetDataHandlers / HandleSpawn force teleport phase 2 {0}", args.Player.Name);
@ -3225,160 +3258,12 @@ namespace TShockAPI
private static bool HandleLoadNetModule(GetDataHandlerArgs args) private static bool HandleLoadNetModule(GetDataHandlerArgs args)
{ {
short moduleId = args.Data.ReadInt16(); short moduleId = args.Data.ReadInt16();
if (moduleId == (int)NetModulesTypes.CreativePowers)
if (OnReadNetModule(args.Player, args.Data, (NetModuleType)moduleId))
{ {
CreativePowerTypes powerId = (CreativePowerTypes)args.Data.ReadInt16(); return true;
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);
}
} }
// 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; return false;
} }
@ -3893,7 +3778,7 @@ namespace TShockAPI
public bool Killed { get; internal set; } public bool Killed { get; internal set; }
} }
public enum NetModulesTypes public enum NetModuleType
{ {
Liquid, Liquid,
Text, Text,

View 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;
}
}
}

View 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;
}
}
}

View 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" },
};
}
}

View 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;
}
}
}

View 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);
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View file

@ -267,6 +267,9 @@ namespace TShockAPI
[Description("User can use wormhole potions.")] [Description("User can use wormhole potions.")]
public static readonly string wormhole = "tshock.tp.wormhole"; public static readonly string wormhole = "tshock.tp.wormhole";
[Description("User can use pylons to teleport")]
public static readonly string pylon = "tshock.tp.pylon";
#endregion #endregion
#region tshock.world nodes #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.")] [Description("User can use Creative UI to set the NPC spawn rate of the world.")]
public static readonly string journey_setspawnrate = "tshock.journey.setspawnrate"; 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 #endregion
#region Non-grouped #region Non-grouped

View file

@ -89,6 +89,14 @@
<Compile Include="DB\TileManager.cs" /> <Compile Include="DB\TileManager.cs" />
<Compile Include="Extensions\ExceptionExt.cs" /> <Compile Include="Extensions\ExceptionExt.cs" />
<Compile Include="Handlers\IPacketHandler.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\SendTileSquareHandler.cs" /> <Compile Include="Handlers\SendTileSquareHandler.cs" />
<Compile Include="Hooks\AccountHooks.cs" /> <Compile Include="Hooks\AccountHooks.cs" />
<Compile Include="Hooks\GeneralHooks.cs" /> <Compile Include="Hooks\GeneralHooks.cs" />
@ -213,7 +221,7 @@
</PropertyGroup> </PropertyGroup>
<ProjectExtensions> <ProjectExtensions>
<VisualStudio> <VisualStudio>
<UserProperties BuildVersion_UpdateAssemblyVersion="True" BuildVersion_UpdateFileVersion="True" BuildVersion_BuildAction="Both" BuildVersion_BuildVersioningStyle="None.None.None.MonthAndDayStamp" BuildVersion_StartDate="2011/6/17" BuildVersion_IncrementBeforeBuild="False" /> <UserProperties BuildVersion_IncrementBeforeBuild="False" BuildVersion_StartDate="2011/6/17" BuildVersion_BuildVersioningStyle="None.None.None.MonthAndDayStamp" BuildVersion_BuildAction="Both" BuildVersion_UpdateFileVersion="True" BuildVersion_UpdateAssemblyVersion="True" />
</VisualStudio> </VisualStudio>
</ProjectExtensions> </ProjectExtensions>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.