Merge branch 'general-devel' into fix-bugs
This commit is contained in:
commit
dff5f043aa
8 changed files with 594 additions and 288 deletions
74
.github/workflows/build.yml
vendored
74
.github/workflows/build.yml
vendored
|
|
@ -5,68 +5,62 @@ on: [push, pull_request]
|
|||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
mode: ["Debug", "Release"]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Git checkout
|
||||
uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install nuget
|
||||
run: choco install nuget.commandline
|
||||
- name: OTAPI Debug
|
||||
shell: cmd
|
||||
- name: Install NuGet client
|
||||
uses: nuget/setup-nuget@v1
|
||||
- name: Restore NuGet packages
|
||||
run: |
|
||||
nuget restore .\TerrariaServerAPI\TShock.4.OTAPI.sln
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" .\TerrariaServerAPI\TShock.4.OTAPI.sln /p:Configuration=Debug
|
||||
cd .\TerrariaServerAPI\TShock.Modifications.Bootstrapper\bin\Debug
|
||||
TShock.Modifications.Bootstrapper.exe
|
||||
- name: OTAPI Release
|
||||
nuget restore TShock.sln
|
||||
- name: Build OTAPI
|
||||
shell: cmd
|
||||
run: |
|
||||
nuget restore .\TerrariaServerAPI\TShock.4.OTAPI.sln
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" .\TerrariaServerAPI\TShock.4.OTAPI.sln /p:Configuration=Release
|
||||
cd .\TerrariaServerAPI\TShock.Modifications.Bootstrapper\bin\Release
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" .\TerrariaServerAPI\TShock.4.OTAPI.sln /p:Configuration=${{ matrix.mode }}
|
||||
cd .\TerrariaServerAPI\TShock.Modifications.Bootstrapper\bin\${{ matrix.mode }}
|
||||
TShock.Modifications.Bootstrapper.exe
|
||||
- name: TerrariaServerAPI Debug
|
||||
- name: Build TerrariaServerAPI
|
||||
shell: cmd
|
||||
run: |
|
||||
cd .\TerrariaServerAPI
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" .\TerrariaServerAPI\TerrariaServerAPI.csproj /p:Configuration=Debug
|
||||
- name: TShock Debug
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" .\TerrariaServerAPI\TerrariaServerAPI.csproj /p:Configuration=${{ matrix.mode }}
|
||||
- name: Build TShock
|
||||
shell: cmd
|
||||
run: |
|
||||
nuget restore TShock.sln
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" .\TShockAPI\TShockAPI.csproj /p:Configuration=Debug
|
||||
- name: TerrariaServerAPI Release
|
||||
shell: cmd
|
||||
run: |
|
||||
cd .\TerrariaServerAPI
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" .\TerrariaServerAPI\TerrariaServerAPI.csproj /p:Configuration=Release
|
||||
- name: TShock Release
|
||||
shell: cmd
|
||||
run: |
|
||||
nuget restore TShock.sln
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" .\TShockAPI\TShockAPI.csproj /p:Configuration=Release
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" .\TShockAPI\TShockAPI.csproj /p:Configuration=${{ matrix.mode }}
|
||||
- name: Normalize release packaging
|
||||
shell: cmd
|
||||
run: |
|
||||
xcopy /Y prebuilts\*.* TShockAPI\bin\Release
|
||||
xcopy /Y prebuilts\*.* TShockAPI\bin\Debug
|
||||
mkdir TShockAPI\bin\Debug\ServerPlugins
|
||||
mkdir TShockAPI\bin\Release\ServerPlugins
|
||||
move TShockAPI\bin\Release\TShockAPI.dll TShockAPI\bin\Release\ServerPlugins
|
||||
move TShockAPI\bin\Debug\TShockAPI.dll TShockAPI\bin\Debug\ServerPlugins
|
||||
- uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: Experimental TShock (not debug)
|
||||
path: TShockAPI\bin\Release
|
||||
- uses: actions/upload-artifact@master
|
||||
xcopy /Y prebuilts\*.* TShockAPI\bin\${{ matrix.mode }}
|
||||
mkdir TShockAPI\bin\${{ matrix.mode }}\ServerPlugins
|
||||
move TShockAPI\bin\${{ matrix.mode }}\TShockAPI.dll TShockAPI\bin\${{ matrix.mode }}\ServerPlugins
|
||||
- name: Upload TShock (Debug)
|
||||
if: contains(matrix.mode, 'Debug')
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: Experimental TShock (debug)
|
||||
path: TShockAPI\bin\Debug
|
||||
- uses: actions/upload-artifact@master
|
||||
- name: Upload OTAPI Bootstrapper (Debug)
|
||||
if: contains(matrix.mode, 'Debug')
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: Experimental (debug) OTAPI Bootstrapper
|
||||
path: .\TerrariaServerAPI\TShock.Modifications.Bootstrapper\bin\Debug\TShock.Modifications.Bootstrapper.exe
|
||||
- uses: actions/upload-artifact@master
|
||||
- name: Upload TShock (Not Debug)
|
||||
if: contains(matrix.mode, 'Release')
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: Experimental TShock (not debug)
|
||||
path: TShockAPI\bin\Release
|
||||
- name: Upload OTAPI Bootstrapper (Not Debug)
|
||||
if: contains(matrix.mode, 'Release')
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: Experimental (not debug) OTAPI Bootstrapper
|
||||
path: .\TerrariaServerAPI\TShock.Modifications.Bootstrapper\bin\Release\TShock.Modifications.Bootstrapper.exe
|
||||
|
|
|
|||
11
CHANGELOG.md
11
CHANGELOG.md
|
|
@ -2,7 +2,14 @@
|
|||
|
||||
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 Release
|
||||
## Upcoming release
|
||||
* Fix all rope coils. (@Olink)
|
||||
* Fixed a longstanding issue with SendTileSquare that could result in desyncs and visual errors. (@QuiCM)
|
||||
* Fixed placement issues with Item Frames, Teleportation Pylons, etc. (@QuiCM)
|
||||
* Doors are good now for real probably (@QuiCM, @Hakusaro, @Olink)
|
||||
* Bump default max damage received cap to 42,000 to accommodate the Empress of Light's instant kill death amount. (@hakusaro, @moisterrific, @Irethia, @Ayrawei)
|
||||
|
||||
## TShock 4.4.0 (Pre-release 9)
|
||||
* Fixed pet licenses. (@Olink)
|
||||
* Added initial support for Journey mode in SSC worlds. (@Olink)
|
||||
* Made TShock database MySQL 8 compatible by escaping column names in our IQueryBuilder code. (Name `Groups` is a reserved element in this version, which is used in our `Region` table.) (@Patrikkk)
|
||||
|
|
@ -28,7 +35,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin
|
|||
* The default group that gets this permission is `Guest` for the time being.
|
||||
* To add this command to your guest group, give them `tshock.synclocalarea`, with `/group addperm guest tshock.synclocalarea`.
|
||||
* This command may be removed at any time in the future (and will likely be removed when send tile square handling is fixed).
|
||||
* Fix all rope coils. (@Olink)
|
||||
* Fixed smart door automatic door desync and deletion issue. (@hakusaro)
|
||||
|
||||
## TShock 4.4.0 (Pre-release 8)
|
||||
* Update for OTAPI 2.0.0.36 and Terraria 1.4.0.4. (@hakusaro, @Patrikkk, @DeathCradle)
|
||||
|
|
|
|||
|
|
@ -36,15 +36,19 @@ namespace TShockAPI
|
|||
/// <summary>Bouncer is the TShock anti-hack and anti-cheat system.</summary>
|
||||
internal sealed class Bouncer
|
||||
{
|
||||
internal Handlers.SendTileSquareHandler STSHandler { 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;
|
||||
|
||||
// Setup hooks
|
||||
GetDataHandlers.GetSection += OnGetSection;
|
||||
GetDataHandlers.PlayerUpdate += OnPlayerUpdate;
|
||||
GetDataHandlers.TileEdit += OnTileEdit;
|
||||
GetDataHandlers.SendTileSquare += OnSendTileSquare;
|
||||
GetDataHandlers.ItemDrop += OnItemDrop;
|
||||
GetDataHandlers.NewProjectile += OnNewProjectile;
|
||||
GetDataHandlers.NPCStrike += OnNPCStrike;
|
||||
|
|
@ -530,206 +534,6 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>Bouncer's SendTileSquare hook halts large scope world destruction.</summary>
|
||||
/// <param name="sender">The object that triggered the event.</param>
|
||||
/// <param name="args">The packet arguments that the event has.</param>
|
||||
internal void OnSendTileSquare(object sender, GetDataHandlers.SendTileSquareEventArgs args)
|
||||
{
|
||||
short size = args.Size;
|
||||
int tileX = args.TileX;
|
||||
int tileY = args.TileY;
|
||||
|
||||
if (args.Player.HasPermission(Permissions.allowclientsideworldedit))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare accepted clientside world edit from {0}", args.Player.Name);
|
||||
args.Handled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// From White:
|
||||
// IIRC it's because 5 means a 5x5 square which is normal for a tile square, and anything bigger is a non-vanilla tile modification attempt
|
||||
if (size > 5)
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected from non-vanilla tilemod from {0}", args.Player.Name);
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Player.IsBouncerThrottled())
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected from throttle from {0}", args.Player.Name);
|
||||
args.Player.SendTileSquare(tileX, tileY, size);
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Player.IsBeingDisabled())
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected from being disabled from {0}", args.Player.Name);
|
||||
args.Player.SendTileSquare(tileX, tileY, size);
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
bool failed = false;
|
||||
try
|
||||
{
|
||||
var tiles = new NetTile[size, size];
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
for (int y = 0; y < size; y++)
|
||||
{
|
||||
tiles[x, y] = new NetTile(args.Data);
|
||||
}
|
||||
}
|
||||
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
int realx = tileX + x;
|
||||
if (realx < 0 || realx >= Main.maxTilesX)
|
||||
continue;
|
||||
|
||||
for (int y = 0; y < size; y++)
|
||||
{
|
||||
int realy = tileY + y;
|
||||
if (realy < 0 || realy >= Main.maxTilesY)
|
||||
continue;
|
||||
|
||||
var tile = Main.tile[realx, realy];
|
||||
var newtile = tiles[x, y];
|
||||
if (!args.Player.HasBuildPermission(realx, realy) ||
|
||||
!args.Player.IsInRange(realx, realy))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Fixes the Flower Boots not creating flowers issue
|
||||
if (size == 1 && args.Player.Accessories.Any(i => i.active && i.netID == ItemID.FlowerBoots))
|
||||
{
|
||||
if (Main.tile[realx, realy + 1].type == TileID.Grass && (newtile.Type == TileID.Plants || newtile.Type == TileID.Plants2))
|
||||
{
|
||||
args.Handled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Main.tile[realx, realy + 1].type == TileID.HallowedGrass && (newtile.Type == TileID.HallowedPlants || newtile.Type == TileID.HallowedPlants2))
|
||||
{
|
||||
args.Handled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Main.tile[realx, realy + 1].type == TileID.JungleGrass && newtile.Type == TileID.JunglePlants2)
|
||||
{
|
||||
args.Handled = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Junction Box
|
||||
if (tile.type == TileID.WirePipe)
|
||||
{
|
||||
args.Handled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Orientable tiles
|
||||
if (tile.type == newtile.Type && orientableTiles.Contains(tile.type))
|
||||
{
|
||||
Main.tile[realx, realy].frameX = newtile.FrameX;
|
||||
Main.tile[realx, realy].frameY = newtile.FrameY;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Landmine
|
||||
if (tile.type == TileID.LandMine && !newtile.Active)
|
||||
{
|
||||
Main.tile[realx, realy].active(false);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Tile entities: sensors, item frames, training dummies
|
||||
// here it handles all tile entities listed in `TileEntityID`
|
||||
if ((newtile.Type == TileID.LogicSensor ||
|
||||
newtile.Type == TileID.ItemFrame ||
|
||||
newtile.Type == TileID.TargetDummy) &&
|
||||
!Main.tile[realx, realy].active())
|
||||
{
|
||||
Main.tile[realx, realy].type = newtile.Type;
|
||||
Main.tile[realx, realy].frameX = newtile.FrameX;
|
||||
Main.tile[realx, realy].frameY = newtile.FrameY;
|
||||
Main.tile[realx, realy].active(true);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (tile.active() && newtile.Active && tile.type != newtile.Type)
|
||||
{
|
||||
// Grass <-> Grass
|
||||
if ((TileID.Sets.Conversion.Grass[tile.type] && TileID.Sets.Conversion.Grass[newtile.Type]) ||
|
||||
// Dirt <-> Dirt
|
||||
((tile.type == 0 || tile.type == 59) &&
|
||||
(newtile.Type == 0 || newtile.Type == 59)) ||
|
||||
// Ice <-> Ice
|
||||
(TileID.Sets.Conversion.Ice[tile.type] && TileID.Sets.Conversion.Ice[newtile.Type]) ||
|
||||
// Stone <-> Stone
|
||||
((TileID.Sets.Conversion.Stone[tile.type] || Main.tileMoss[tile.type]) &&
|
||||
(TileID.Sets.Conversion.Stone[newtile.Type] || Main.tileMoss[newtile.Type])) ||
|
||||
// Sand <-> Sand
|
||||
(TileID.Sets.Conversion.Sand[tile.type] && TileID.Sets.Conversion.Sand[newtile.Type]) ||
|
||||
// Sandstone <-> Sandstone
|
||||
(TileID.Sets.Conversion.Sandstone[tile.type] && TileID.Sets.Conversion.Sandstone[newtile.Type]) ||
|
||||
// Hardened Sand <-> Hardened Sand
|
||||
(TileID.Sets.Conversion.HardenedSand[tile.type] && TileID.Sets.Conversion.HardenedSand[newtile.Type]))
|
||||
{
|
||||
Main.tile[realx, realy].type = newtile.Type;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Stone wall <-> Stone wall
|
||||
if (((tile.wall == 1 || tile.wall == 3 || tile.wall == 28 || tile.wall == 83) &&
|
||||
(newtile.Wall == 1 || newtile.Wall == 3 || newtile.Wall == 28 || newtile.Wall == 83)) ||
|
||||
// Leaf wall <-> Leaf wall
|
||||
(((tile.wall >= 63 && tile.wall <= 70) || tile.wall == 81) &&
|
||||
((newtile.Wall >= 63 && newtile.Wall <= 70) || newtile.Wall == 81)))
|
||||
{
|
||||
Main.tile[realx, realy].wall = newtile.Wall;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ((tile.type == TileID.TrapdoorClosed && (newtile.Type == TileID.TrapdoorOpen || !newtile.Active)) ||
|
||||
(tile.type == TileID.TrapdoorOpen && (newtile.Type == TileID.TrapdoorClosed || !newtile.Active)) ||
|
||||
(!tile.active() && newtile.Active && (newtile.Type == TileID.TrapdoorOpen || newtile.Type == TileID.TrapdoorClosed)))
|
||||
{
|
||||
Main.tile[realx, realy].type = newtile.Type;
|
||||
Main.tile[realx, realy].frameX = newtile.FrameX;
|
||||
Main.tile[realx, realy].frameY = newtile.FrameY;
|
||||
Main.tile[realx, realy].active(newtile.Active);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
TSPlayer.All.SendTileSquare(tileX, tileY, size + 1);
|
||||
WorldGen.RangeFrame(tileX, tileY, tileX + size, tileY + size);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Player.SendTileSquare(tileX, tileY, size);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
args.Player.SendTileSquare(tileX, tileY, size);
|
||||
failed = true;
|
||||
}
|
||||
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare from {0} {1} {2}", args.Player.Name, changed, failed);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>Registered when items fall to the ground to prevent cheating.</summary>
|
||||
/// <param name="sender">The object that triggered the event.</param>
|
||||
/// <param name="args">The packet arguments that the event has.</param>
|
||||
|
|
@ -2049,7 +1853,7 @@ namespace TShockAPI
|
|||
short id = args.PlayerId;
|
||||
PlayerDeathReason playerDeathReason = args.PlayerDeathReason;
|
||||
|
||||
if (damage > 20000) //Abnormal values have the potential to cause infinite loops in the server.
|
||||
if (damage > 42000) //Abnormal values have the potential to cause infinite loops in the server.
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / OnKillMe rejected high damage from {0} {1}", args.Player.Name, damage);
|
||||
args.Player.Kick("Failed to shade polygon normals.", true, true);
|
||||
|
|
|
|||
|
|
@ -2317,14 +2317,14 @@ namespace TShockAPI
|
|||
{
|
||||
var player = args.Player;
|
||||
var size = args.Data.ReadInt16();
|
||||
|
||||
var changeType = TileChangeType.None;
|
||||
|
||||
bool hasChangeType = ((size & 0x7FFF) & 0x8000) != 0;
|
||||
if (hasChangeType)
|
||||
{
|
||||
changeType = (TileChangeType)args.Data.ReadInt8();
|
||||
}
|
||||
|
||||
|
||||
var tileX = args.Data.ReadInt16();
|
||||
var tileY = args.Data.ReadInt16();
|
||||
var data = args.Data;
|
||||
|
|
|
|||
504
TShockAPI/Handlers/SendTileSquareHandler.cs
Normal file
504
TShockAPI/Handlers/SendTileSquareHandler.cs
Normal file
|
|
@ -0,0 +1,504 @@
|
|||
using OTAPI.Tile;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Terraria;
|
||||
using Terraria.DataStructures;
|
||||
using Terraria.GameContent.Tile_Entities;
|
||||
using Terraria.ID;
|
||||
using Terraria.ObjectData;
|
||||
using TShockAPI.Net;
|
||||
|
||||
namespace TShockAPI.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides processors for handling Tile Square packets
|
||||
/// </summary>
|
||||
public class SendTileSquareHandler
|
||||
{
|
||||
/// <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>>
|
||||
{
|
||||
{ TileID.Grass, new List<ushort> { TileID.Plants, TileID.Plants2 } },
|
||||
{ TileID.HallowedGrass, new List<ushort> { TileID.HallowedPlants, TileID.HallowedPlants2 } },
|
||||
{ TileID.JungleGrass, new List<ushort> { TileID.JunglePlants, TileID.JunglePlants2 } }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Item IDs that can spawn flowers while you walk
|
||||
/// </summary>
|
||||
List<int> _flowerBootItems = new List<int>
|
||||
{
|
||||
ItemID.FlowerBoots,
|
||||
ItemID.FairyBoots
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Maps TileIDs to Tile Entity IDs.
|
||||
/// 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>
|
||||
{
|
||||
{ TileID.TargetDummy, TETrainingDummy._myEntityID },
|
||||
{ TileID.ItemFrame, TEItemFrame._myEntityID },
|
||||
{ TileID.LogicSensor, TELogicSensor._myEntityID },
|
||||
{ TileID.DisplayDoll, TEDisplayDoll._myEntityID },
|
||||
{ TileID.WeaponsRack2, TEWeaponsRack._myEntityID },
|
||||
{ TileID.HatRack, TEHatRack._myEntityID },
|
||||
{ TileID.FoodPlatter, TEFoodPlatter._myEntityID },
|
||||
{ TileID.TeleportationPylon, TETeleportationPylon._myEntityID }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a SendTileSquare packet is received
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
public void OnReceiveSendTileSquare(object sender, GetDataHandlers.SendTileSquareEventArgs args)
|
||||
{
|
||||
// By default, we'll handle everything
|
||||
args.Handled = true;
|
||||
|
||||
if (ShouldSkipProcessing(args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool[,] processed = new bool[args.Size, args.Size];
|
||||
NetTile[,] tiles = ReadNetTilesFromStream(args.Data, args.Size);
|
||||
|
||||
Debug.VisualiseTileSetDiff(args.TileX, args.TileY, args.Size, args.Size, tiles);
|
||||
|
||||
IterateTileSquare(tiles, processed, args);
|
||||
|
||||
// Uncommenting this function will send the same tile square 10 blocks above you for visualisation. This will modify your world and overwrite existing blocks.
|
||||
// Use in test worlds only.
|
||||
//Debug.DisplayTileSetInGame(tileX, tileY - 10, size, size, tiles, args.Player);
|
||||
|
||||
// If we are handling this event then we have updated the server's Main.tile state the way we want it.
|
||||
// At this point we should send our state back to the client so they remain in sync with the server
|
||||
if (args.Handled == true)
|
||||
{
|
||||
args.Player.SendTileSquare(args.TileX, args.TileY, args.Size);
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare reimplemented from carbonara from {0}", args.Player.Name);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterates over each tile in the tile square and performs processing on individual tiles or multi-tile Tile Objects
|
||||
/// </summary>
|
||||
/// <param name="tiles"></param>
|
||||
/// <param name="processed"></param>
|
||||
/// <param name="args"></param>
|
||||
internal void IterateTileSquare(NetTile[,] tiles, bool[,] processed, GetDataHandlers.SendTileSquareEventArgs args)
|
||||
{
|
||||
short size = args.Size;
|
||||
int tileX = args.TileX;
|
||||
int tileY = args.TileY;
|
||||
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
for (int y = 0; y < size; y++)
|
||||
{
|
||||
// Do not process already processed tiles
|
||||
if (processed[x, y])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int realX = tileX + x;
|
||||
int realY = tileY + y;
|
||||
|
||||
// Do not process tiles outside of the world boundaries
|
||||
if ((realX < 0 || realX >= Main.maxTilesX)
|
||||
|| (realY < 0 || realY > Main.maxTilesY))
|
||||
{
|
||||
processed[x, y] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do not process tiles that the player cannot update
|
||||
if (!args.Player.HasBuildPermission(realX, realY) ||
|
||||
!args.Player.IsInRange(realX, realY))
|
||||
{
|
||||
processed[x, y] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
NetTile newTile = tiles[x, y];
|
||||
TileObjectData data;
|
||||
|
||||
// If the new tile has an associated TileObjectData object, we take the tile and the surrounding tiles that make up the tile object
|
||||
// and process them as a tile object
|
||||
if (newTile.Type < TileObjectData._data.Count && TileObjectData._data[newTile.Type] != null)
|
||||
{
|
||||
data = TileObjectData._data[newTile.Type];
|
||||
NetTile[,] newTiles;
|
||||
int width = data.Width;
|
||||
int height = data.Height;
|
||||
int offset = 0;
|
||||
|
||||
if (newTile.Type == TileID.TrapdoorClosed)
|
||||
{
|
||||
// Trapdoors can modify a 2x3 space. When it closes it will have leftover tiles either on top or bottom.
|
||||
// If we don't update these tiles, the trapdoor gets confused and disappears.
|
||||
// So we capture all 6 possible tiles and offset ourselves 1 tile above the closed trapdoor to capture the entire 2x3 area
|
||||
width = 2;
|
||||
height = 3;
|
||||
offset = -1;
|
||||
}
|
||||
|
||||
newTiles = new NetTile[width, height];
|
||||
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
newTiles[i, j] = tiles[x + i, y + j + offset];
|
||||
processed[x + i, y + j] = true;
|
||||
}
|
||||
}
|
||||
ProcessTileObject(newTile.Type, realX, realY + offset, width, height, newTiles, args);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the new tile does not have an associated tile object, process it as an individual tile
|
||||
ProcessSingleTile(realX, realY, newTile, size, args);
|
||||
processed[x, y] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a tile object consisting of multiple tiles from the tile square packet
|
||||
/// </summary>
|
||||
/// <param name="tileType">The tile type the object is comprised of</param>
|
||||
/// <param name="newTiles">2D array of NetTile containing the new tiles properties</param>
|
||||
/// <param name="realX">X position at the top left of the object</param>
|
||||
/// <param name="realY">Y position at the top left of the object</param>
|
||||
/// <param name="width">Width of the tile object</param>
|
||||
/// <param name="height">Height of the tile object</param>
|
||||
/// <param name="args">SendTileSquareEventArgs containing event information</param>
|
||||
internal void ProcessTileObject(int tileType, int realX, int realY, int width, int height, NetTile[,] newTiles, GetDataHandlers.SendTileSquareEventArgs args)
|
||||
{
|
||||
// As long as the player has permission to build, we should allow a tile object to be placed
|
||||
// More in depth checks should take place in handlers for the Place Object (79), Update Tile Entity (86), and Place Tile Entity (87) packets
|
||||
if (!args.Player.HasBuildPermissionForTileObject(realX, realY, width, height))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected from no permission for tile object from {0}", args.Player.Name);
|
||||
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))
|
||||
{
|
||||
TileEntity.PlaceEntityNet(realX, realY, _tileEntityIdToTileIdMap[tileType]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a single tile from the tile square packet
|
||||
/// </summary>
|
||||
/// <param name="realX">X position at the top left of the object</param>
|
||||
/// <param name="realY">Y position at the top left of the object</param>
|
||||
/// <param name="newTile">The NetTile containing new tile properties</param>
|
||||
/// <param name="squareSize">The size of the tile square being received</param>
|
||||
/// <param name="args">SendTileSquareEventArgs containing event information</param>
|
||||
internal void ProcessSingleTile(int realX, int realY, NetTile newTile, int squareSize, GetDataHandlers.SendTileSquareEventArgs args)
|
||||
{
|
||||
// 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)))
|
||||
{
|
||||
ProcessFlowerBoots(realX, realY, newTile, args);
|
||||
return;
|
||||
}
|
||||
|
||||
ITile tile = Main.tile[realX, realY];
|
||||
|
||||
if (tile.type == TileID.LandMine && !newTile.Active)
|
||||
{
|
||||
UpdateServerTileState(tile, newTile);
|
||||
}
|
||||
|
||||
if (tile.type == TileID.WirePipe)
|
||||
{
|
||||
UpdateServerTileState(tile, newTile);
|
||||
}
|
||||
|
||||
ProcessConversionSpreads(Main.tile[realX, realY], newTile);
|
||||
|
||||
// All other single tile updates should not be processed.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies changes to a tile if a tile square for flower-growing boots is valid
|
||||
/// </summary>
|
||||
/// <param name="realX">The tile x position of the tile square packet - this is where the flowers are intending to grow</param>
|
||||
/// <param name="realY">The tile y position of the tile square packet - this is where the flowers are intending to grow</param>
|
||||
/// <param name="newTile">The NetTile containing information about the flowers that are being grown</param>
|
||||
/// <param name="args">SendTileSquareEventArgs containing event information</param>
|
||||
internal void ProcessFlowerBoots(int realX, int realY, NetTile newTile, GetDataHandlers.SendTileSquareEventArgs args)
|
||||
{
|
||||
// We need to get the tile below the tile square to determine what grass types are allowed
|
||||
if (!WorldGen.InWorld(realX, realY + 1))
|
||||
{
|
||||
// If the tile below the tile square isn't valid, we return here and don't update the server tile state
|
||||
return;
|
||||
}
|
||||
|
||||
ITile tile = Main.tile[realX, realY + 1];
|
||||
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;
|
||||
}
|
||||
|
||||
UpdateServerTileState(Main.tile[realX, realY], newTile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a single tile on the server if it is a valid conversion from one tile or wall type to another (eg stone -> corrupt stone)
|
||||
/// </summary>
|
||||
/// <param name="tile">The tile to update</param>
|
||||
/// <param name="newTile">The NetTile containing new tile properties</param>
|
||||
internal void ProcessConversionSpreads(ITile tile, NetTile newTile)
|
||||
{
|
||||
// Update if the existing tile or wall is convertible and the new tile or wall is a valid conversion
|
||||
if (
|
||||
((TileID.Sets.Conversion.Stone[tile.type] || Main.tileMoss[tile.type]) && (TileID.Sets.Conversion.Stone[newTile.Type] || Main.tileMoss[newTile.Type])) ||
|
||||
((tile.type == 0 || tile.type == 59) && (newTile.Type == 0 || newTile.Type == 59)) ||
|
||||
TileID.Sets.Conversion.Grass[tile.type] && TileID.Sets.Conversion.Grass[newTile.Type] ||
|
||||
TileID.Sets.Conversion.Ice[tile.type] && TileID.Sets.Conversion.Ice[newTile.Type] ||
|
||||
TileID.Sets.Conversion.Sand[tile.type] && TileID.Sets.Conversion.Sand[newTile.Type] ||
|
||||
TileID.Sets.Conversion.Sandstone[tile.type] && TileID.Sets.Conversion.Sandstone[newTile.Type] ||
|
||||
TileID.Sets.Conversion.HardenedSand[tile.type] && TileID.Sets.Conversion.HardenedSand[newTile.Type] ||
|
||||
TileID.Sets.Conversion.Thorn[tile.type] && TileID.Sets.Conversion.Thorn[newTile.Type] ||
|
||||
TileID.Sets.Conversion.Moss[tile.type] && TileID.Sets.Conversion.Moss[newTile.Type] ||
|
||||
TileID.Sets.Conversion.MossBrick[tile.type] && TileID.Sets.Conversion.MossBrick[newTile.Type] ||
|
||||
WallID.Sets.Conversion.Stone[tile.wall] && WallID.Sets.Conversion.Stone[newTile.Wall] ||
|
||||
WallID.Sets.Conversion.Grass[tile.wall] && WallID.Sets.Conversion.Grass[newTile.Wall] ||
|
||||
WallID.Sets.Conversion.Sandstone[tile.wall] && WallID.Sets.Conversion.Sandstone[newTile.Wall] ||
|
||||
WallID.Sets.Conversion.HardenedSand[tile.wall] && WallID.Sets.Conversion.HardenedSand[newTile.Wall] ||
|
||||
WallID.Sets.Conversion.PureSand[tile.wall] && WallID.Sets.Conversion.PureSand[newTile.Wall] ||
|
||||
WallID.Sets.Conversion.NewWall1[tile.wall] && WallID.Sets.Conversion.NewWall1[newTile.Wall] ||
|
||||
WallID.Sets.Conversion.NewWall2[tile.wall] && WallID.Sets.Conversion.NewWall2[newTile.Wall] ||
|
||||
WallID.Sets.Conversion.NewWall3[tile.wall] && WallID.Sets.Conversion.NewWall3[newTile.Wall] ||
|
||||
WallID.Sets.Conversion.NewWall4[tile.wall] && WallID.Sets.Conversion.NewWall4[newTile.Wall]
|
||||
)
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare processing a conversion update - [{0}|{1}] -> [{2}|{3}]", tile.type, tile.wall, newTile.Type, newTile.Wall);
|
||||
UpdateServerTileState(tile, newTile);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a single tile's world state with a change from the tile square packet
|
||||
/// </summary>
|
||||
/// <param name="tile">The tile to update</param>
|
||||
/// <param name="newTile">The NetTile containing the change</param>
|
||||
public static void UpdateServerTileState(ITile tile, NetTile newTile)
|
||||
{
|
||||
tile.active(newTile.Active);
|
||||
tile.type = newTile.Type;
|
||||
|
||||
if (newTile.FrameImportant)
|
||||
{
|
||||
tile.frameX = newTile.FrameX;
|
||||
tile.frameY = newTile.FrameY;
|
||||
}
|
||||
|
||||
if (newTile.HasWall)
|
||||
{
|
||||
tile.wall = newTile.Wall;
|
||||
}
|
||||
|
||||
if (newTile.HasLiquid)
|
||||
{
|
||||
tile.liquid = newTile.Liquid;
|
||||
tile.liquidType(newTile.LiquidType);
|
||||
}
|
||||
|
||||
tile.wire(newTile.Wire);
|
||||
tile.wire2(newTile.Wire2);
|
||||
tile.wire3(newTile.Wire3);
|
||||
tile.wire4(newTile.Wire4);
|
||||
|
||||
tile.halfBrick(newTile.IsHalf);
|
||||
|
||||
if (newTile.HasColor)
|
||||
{
|
||||
tile.color(newTile.TileColor);
|
||||
}
|
||||
|
||||
if (newTile.HasWallColor)
|
||||
{
|
||||
tile.wallColor(newTile.WallColor);
|
||||
}
|
||||
|
||||
byte slope = 0;
|
||||
if (newTile.Slope)
|
||||
{
|
||||
slope += 1;
|
||||
}
|
||||
if (newTile.Slope2)
|
||||
{
|
||||
slope += 2;
|
||||
}
|
||||
if (newTile.Slope3)
|
||||
{
|
||||
slope += 4;
|
||||
}
|
||||
|
||||
tile.slope(slope);
|
||||
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare updated a tile from type {0} to {1}", tile.type, newTile.Type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs <see cref="UpdateServerTileState(ITile, NetTile)"/> on multiple tiles
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="newTiles"></param>
|
||||
public static void UpdateMultipleServerTileStates(int x, int y, int width, int height, NetTile[,] newTiles)
|
||||
{
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
UpdateServerTileState(Main.tile[x + i, y + j], newTiles[i, j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a set of NetTiles from a memory stream
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="size"></param>
|
||||
/// <returns></returns>
|
||||
static NetTile[,] ReadNetTilesFromStream(System.IO.MemoryStream stream, int size)
|
||||
{
|
||||
NetTile[,] tiles = new NetTile[size, size];
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
for (int y = 0; y < size; y++)
|
||||
{
|
||||
tiles[x, y] = new NetTile(stream);
|
||||
}
|
||||
}
|
||||
|
||||
return tiles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether or not the tile square should be immediately accepted or rejected
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
static bool ShouldSkipProcessing(GetDataHandlers.SendTileSquareEventArgs args)
|
||||
{
|
||||
if (args.Player.HasPermission(Permissions.allowclientsideworldedit))
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare accepted clientside world edit from {0}", args.Player.Name);
|
||||
args.Handled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 5x5 is the largest vanilla-sized tile square. Anything larger than this should not be seen in the vanilla game and should be rejected
|
||||
if (args.Size > 5)
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected from non-vanilla tilemod from {0}", args.Player.Name);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.Player.IsBouncerThrottled())
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected from throttle from {0}", args.Player.Name);
|
||||
args.Player.SendTileSquare(args.TileX, args.TileY, args.Size);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.Player.IsBeingDisabled())
|
||||
{
|
||||
TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected from being disabled from {0}", args.Player.Name);
|
||||
args.Player.SendTileSquare(args.TileX, args.TileY, args.Size);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
class Debug
|
||||
{
|
||||
/// <summary>
|
||||
/// Displays the difference in IDs between existing tiles and a set of NetTiles to the console
|
||||
/// </summary>
|
||||
/// <param name="tileX">X position at the top left of the square</param>
|
||||
/// <param name="tileY">Y position at the top left of the square</param>
|
||||
/// <param name="width">Width of the NetTile set</param>
|
||||
/// <param name="height">Height of the NetTile set</param>
|
||||
/// <param name="newTiles">New tiles to be visualised</param>
|
||||
public static void VisualiseTileSetDiff(int tileX, int tileY, int width, int height, NetTile[,] newTiles)
|
||||
{
|
||||
if (TShock.Config.DebugLogs)
|
||||
{
|
||||
char pad = '0';
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
int realY = y + tileY;
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
int realX = x + tileX;
|
||||
ushort type = Main.tile[realX, realY].type;
|
||||
string type2 = type.ToString();
|
||||
Console.Write((type2.ToString()).PadLeft(3, pad) + (Main.tile[realX, realY].active() ? "a" : "-") + " ");
|
||||
}
|
||||
Console.Write(" -> ");
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
int realX = x + tileX;
|
||||
ushort type = newTiles[x, y].Type;
|
||||
string type2 = type.ToString();
|
||||
Console.Write((type2.ToString()).PadLeft(3, pad) + (newTiles[x, y].Active ? "a" : "-") + " ");
|
||||
}
|
||||
Console.Write("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a tile square at the given (tileX, tileY) coordinate, using the given set of NetTiles information to update the tile square
|
||||
/// </summary>
|
||||
/// <param name="tileX">X position at the top left of the square</param>
|
||||
/// <param name="tileY">Y position at the top left of the square</param>
|
||||
/// <param name="width">Width of the NetTile set</param>
|
||||
/// <param name="height">Height of the NetTile set</param>
|
||||
/// <param name="newTiles">New tiles to place in the square</param>
|
||||
/// <param name="player">Player to send the debug display to</param>
|
||||
public static void DisplayTileSetInGame(int tileX, int tileY, int width, int height, NetTile[,] newTiles, TSPlayer player)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
UpdateServerTileState(Main.tile[tileX + x, tileY + y], newTiles[x, y]);
|
||||
}
|
||||
//Add a line of dirt blocks at the bottom for safety
|
||||
UpdateServerTileState(Main.tile[tileX + x, tileY + height], new NetTile { Active = true, Type = 0 });
|
||||
}
|
||||
|
||||
player.SendTileSquare(tileX, tileY, Math.Max(width, height) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -36,6 +36,7 @@ namespace TShockAPI.Net
|
|||
public bool Wire { get; set; }
|
||||
public bool Wire2 { get; set; }
|
||||
public bool Wire3 { get; set; }
|
||||
public bool Wire4 { get; set; }
|
||||
public byte HalfBrick { get; set; }
|
||||
public byte Actuator { get; set; }
|
||||
public bool Inactive { get; set; }
|
||||
|
|
@ -83,6 +84,7 @@ namespace TShockAPI.Net
|
|||
Wire = false;
|
||||
Wire2 = false;
|
||||
Wire3 = false;
|
||||
Wire4 = false;
|
||||
HalfBrick = 0;
|
||||
Actuator = 0;
|
||||
Inactive = false;
|
||||
|
|
@ -151,8 +153,10 @@ namespace TShockAPI.Net
|
|||
if (Slope3)
|
||||
bits[6] = true;
|
||||
|
||||
if (Wire4)
|
||||
bits[7] = true;
|
||||
|
||||
stream.WriteInt8((byte)bits);
|
||||
stream.WriteByte(bits);
|
||||
|
||||
if (HasColor)
|
||||
{
|
||||
|
|
@ -175,7 +179,7 @@ namespace TShockAPI.Net
|
|||
}
|
||||
|
||||
if (HasWall)
|
||||
stream.WriteInt16((short)Wall);;
|
||||
stream.WriteInt16((short)Wall);
|
||||
|
||||
if (HasLiquid)
|
||||
{
|
||||
|
|
@ -194,6 +198,7 @@ namespace TShockAPI.Net
|
|||
Slope = flags2[4];
|
||||
Slope2 = flags2[5];
|
||||
Slope3 = flags2[6];
|
||||
Wire4 = flags2[7];
|
||||
|
||||
if (flags2[2])
|
||||
{
|
||||
|
|
|
|||
|
|
@ -620,7 +620,8 @@ namespace TShockAPI
|
|||
|
||||
/// <summary>Determines if the player can build on a given point.</summary>
|
||||
/// <param name="x">The x coordinate they want to build at.</param>
|
||||
/// <param name="y">The y coordinate they want to paint at.</param>
|
||||
/// <param name="y">The y coordinate they want to build at.</param>
|
||||
/// <param name="shouldWarnPlayer">Whether or not the player should be warned if their build attempt fails</param>
|
||||
/// <returns>True if the player can build at the given point from build, spawn, and region protection.</returns>
|
||||
public bool HasBuildPermission(int x, int y, bool shouldWarnPlayer = true)
|
||||
{
|
||||
|
|
@ -679,6 +680,32 @@ namespace TShockAPI
|
|||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the player can build a multi-block tile object on a given point.
|
||||
/// Tile objects include things like Doors, Trap Doors, Item Frames, Beds, and Dressers.
|
||||
/// </summary>
|
||||
/// <param name="x">The x coordinate they want to build at.</param>
|
||||
/// <param name="y">The y coordinate they want to build at.</param>
|
||||
/// <param name="width">The width of the tile object</param>
|
||||
/// <param name="height">The height of the tile object</param>
|
||||
/// <param name="shouldWarnPlayer">Whether or not the player should be warned if their build attempt fails</param>
|
||||
/// <returns>True if the player can build at the given point from build, spawn, and region protection.</returns>
|
||||
public bool HasBuildPermissionForTileObject(int x, int y, int width, int height, bool shouldWarnPlayer = true)
|
||||
{
|
||||
for (int realx = x; realx < x + width; realx++)
|
||||
{
|
||||
for (int realy = y; realy < y + height; realy++)
|
||||
{
|
||||
if (!HasBuildPermission(realx, realy, shouldWarnPlayer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Determines if the player can paint on a given point. Checks general build permissions, then paint.</summary>
|
||||
/// <param name="x">The x coordinate they want to paint at.</param>
|
||||
/// <param name="y">The y coordinate they want to paint at.</param>
|
||||
|
|
@ -1241,50 +1268,14 @@ namespace TShockAPI
|
|||
/// <param name="x">The x coordinate to send.</param>
|
||||
/// <param name="y">The y coordinate to send.</param>
|
||||
/// <param name="size">The size square set of tiles to send.</param>
|
||||
/// <returns>Status if the tile square was sent successfully (i.e. no exceptions).</returns>
|
||||
/// <returns>true if the tile square was sent successfully, else false</returns>
|
||||
public virtual bool SendTileSquare(int x, int y, int size = 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
int num = (size - 1) / 2;
|
||||
int m_x = 0;
|
||||
int m_y = 0;
|
||||
|
||||
if (x - num < 0)
|
||||
{
|
||||
m_x = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_x = x - num;
|
||||
}
|
||||
|
||||
if (y - num < 0)
|
||||
{
|
||||
m_y = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_y = y - num;
|
||||
}
|
||||
|
||||
if (m_x + size > Main.maxTilesX)
|
||||
{
|
||||
m_x = Main.maxTilesX - size;
|
||||
}
|
||||
|
||||
if (m_y + size > Main.maxTilesY)
|
||||
{
|
||||
m_y = Main.maxTilesY - size;
|
||||
}
|
||||
|
||||
SendData(PacketTypes.TileSendSquare, "", size, m_x, m_y);
|
||||
SendData(PacketTypes.TileSendSquare, "", size, x, y);
|
||||
return true;
|
||||
}
|
||||
catch (IndexOutOfRangeException)
|
||||
{
|
||||
// This is expected if square exceeds array.
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TShock.Log.Error(ex.ToString());
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@
|
|||
<Compile Include="DB\ResearchDatastore.cs" />
|
||||
<Compile Include="DB\TileManager.cs" />
|
||||
<Compile Include="Extensions\ExceptionExt.cs" />
|
||||
<Compile Include="Handlers\SendTileSquareHandler.cs" />
|
||||
<Compile Include="Hooks\AccountHooks.cs" />
|
||||
<Compile Include="Hooks\GeneralHooks.cs" />
|
||||
<Compile Include="Hooks\PlayerHooks.cs" />
|
||||
|
|
@ -211,7 +212,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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue