Merge branch 'general-devel' into handlesynccavernmonster
This commit is contained in:
commit
f01274164f
19 changed files with 624 additions and 181 deletions
|
|
@ -37,13 +37,21 @@ 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; }
|
||||
|
||||
/// <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.OnReceiveEmoji;
|
||||
|
||||
// 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,7 +151,8 @@ namespace TShockAPI
|
|||
{ PacketTypes.CrystalInvasionStart, HandleOldOnesArmy },
|
||||
{ PacketTypes.PlayerHurtV2, HandlePlayerDamageV2 },
|
||||
{ PacketTypes.PlayerDeathV2, HandlePlayerKillMeV2 },
|
||||
{ PacketTypes.SyncRevengeMarker, HandleSyncRevengeMarker },
|
||||
{ PacketTypes.Emoji, HandleEmoji },
|
||||
{ PacketTypes.SyncRevengeMarker, HandleSyncRevengeMarker },
|
||||
{ PacketTypes.FishOutNPC, HandleFishOutNPC },
|
||||
{ PacketTypes.FoodPlatterTryPlacing, HandleFoodPlatterTryPlacing },
|
||||
{ PacketTypes.SyncCavernMonsterType, HandleSyncCavernMonsterType }
|
||||
|
|
@ -1867,6 +1868,40 @@ namespace TShockAPI
|
|||
return args.Handled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 FishOutNPC event.
|
||||
/// </summary>
|
||||
|
|
@ -1952,6 +1987,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)
|
||||
|
|
@ -2209,11 +2278,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);
|
||||
|
|
@ -2222,7 +2290,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);
|
||||
|
|
@ -2929,21 +2997,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;
|
||||
}
|
||||
|
||||
|
|
@ -3226,160 +3294,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;
|
||||
}
|
||||
|
||||
|
|
@ -3577,7 +3497,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;
|
||||
}
|
||||
|
||||
|
|
@ -3671,8 +3591,19 @@ namespace TShockAPI
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HandleEmoji(GetDataHandlerArgs args)
|
||||
{
|
||||
byte playerIndex = args.Data.ReadInt8();
|
||||
byte emojiID = args.Data.ReadInt8();
|
||||
|
||||
private static bool HandleSyncRevengeMarker(GetDataHandlerArgs args)
|
||||
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();
|
||||
|
|
@ -3683,8 +3614,8 @@ namespace TShockAPI
|
|||
int coinsValue = args.Data.ReadInt32();
|
||||
float baseValue = args.Data.ReadSingle();
|
||||
bool spawnedFromStatus = args.Data.ReadBoolean();
|
||||
|
||||
return false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HandleFishOutNPC(GetDataHandlerArgs args)
|
||||
|
|
@ -3901,7 +3832,7 @@ namespace TShockAPI
|
|||
public bool Killed { get; internal set; }
|
||||
}
|
||||
|
||||
public enum NetModulesTypes
|
||||
public enum NetModuleType
|
||||
{
|
||||
Liquid,
|
||||
Text,
|
||||
|
|
|
|||
31
TShockAPI/Handlers/EmojiHandler.cs
Normal file
31
TShockAPI/Handlers/EmojiHandler.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TShockAPI.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles emoji packets and checks for validity and permissions
|
||||
/// </summary>
|
||||
public class EmojiHandler
|
||||
{
|
||||
public void OnReceiveEmoji(object sender, GetDataHandlers.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);
|
||||
}
|
||||
}
|
||||
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,16 @@
|
|||
<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\SendTileSquareHandler.cs" />
|
||||
<Compile Include="Hooks\AccountHooks.cs" />
|
||||
<Compile Include="Hooks\GeneralHooks.cs" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue