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.
## 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)
* Fix all rope coils. (@Olink)

View file

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

View file

@ -1951,6 +1951,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)
@ -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)))
{
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);
@ -2221,7 +2254,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);
@ -3225,160 +3258,12 @@ namespace TShockAPI
private static bool HandleLoadNetModule(GetDataHandlerArgs args)
{
short moduleId = args.Data.ReadInt16();
if (moduleId == (int)NetModulesTypes.CreativePowers)
{
CreativePowerTypes powerId = (CreativePowerTypes)args.Data.ReadInt16();
switch (powerId)
{
case CreativePowerTypes.FreezeTime:
{
if (!args.Player.HasPermission(Permissions.journey_timefreeze))
{
args.Player.SendErrorMessage("You 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???
if (OnReadNetModule(args.Player, args.Data, (NetModuleType)moduleId))
{
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;
}
@ -3893,7 +3778,7 @@ namespace TShockAPI
public bool Killed { get; internal set; }
}
public enum NetModulesTypes
public enum NetModuleType
{
Liquid,
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.")]
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

View file

@ -89,6 +89,14 @@
<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\SendTileSquareHandler.cs" />
<Compile Include="Hooks\AccountHooks.cs" />
<Compile Include="Hooks\GeneralHooks.cs" />
@ -213,7 +221,7 @@
</PropertyGroup>
<ProjectExtensions>
<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>
</ProjectExtensions>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.