/// <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 / SendTileRect processing a conversion update - [{0}|{1}] -> [{2}|{3}]", tile.type, tile.wall, newTile.Type, newTile.Wall); UpdateServerTileState(tile, newTile); } }
/// <summary> /// Processes a single tile from the tile rect 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="rectWidth">The width of the rectangle being processed</param> /// <param name="rectLength">The length of the rectangle being processed</param> /// <param name="args">SendTileRectEventArgs containing event information</param> internal void ProcessSingleTile(int realX, int realY, NetTile newTile, byte rectWidth, byte rectLength, GetDataHandlers.SendTileRectEventArgs args) { // Some boots allow growing flowers on grass. This process sends a 1x1 tile rect to grow the flowers // The rect size must be 1 and the player must have an accessory that allows growing flowers in order for this rect to be valid if (rectWidth == 1 && rectLength == 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> /// Updates a single tile's world state with a change from the tile rect 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 / SendTileRect updated a tile from type {0} to {1}", tile.type, newTile.Type); }
private void OnSendTileSquare(TSPlayer player, BinaryReader reader) { var size = reader.ReadUInt16(); if (size != 1) { return; } reader.ReadInt16(); reader.ReadInt16(); var tile = new NetTile(reader.BaseStream); if (tile.Type == TileID.Mannequin || tile.Type == TileID.Womannequin) { var baseFrameX = tile.FrameX / 100; ILookup <int, Item> lookup; if (tile.FrameY == 0) { lookup = _headSlotLookup; } else if (tile.FrameY == 18) { lookup = _bodySlotLookup; } else { lookup = _legSlotLookup; } var armorItem = lookup[baseFrameX].FirstOrDefault(); if (armorItem != null) { var balanceSheet = player.GetOrCreateBalanceSheet(); balanceSheet.AddTransaction(Transaction.WorldSlot, armorItem.type, -1); } } else if (tile.Type == TileID.WeaponsRack) { var baseFrameX = tile.FrameX - 5000 * (tile.FrameX / 5000); // When placing a weapon an a weapon rack, the client will send two SendTileSquare packets; the first // gives the item ID, and the second gives the item prefix. We know which one is the item ID since the // base frameX is over 100. if (baseFrameX > 100) { player.SetWeaponRackItemId(baseFrameX - 100); } else { var balanceSheet = player.GetOrCreateBalanceSheet(); var weaponRackItemId = player.GetWeaponRackItemId(); balanceSheet.AddTransaction(Transaction.WorldSlot, weaponRackItemId, -1, (byte)baseFrameX); } } }
/// <summary> /// Reads a set of NetTiles from a memory stream /// </summary> /// <param name="stream"></param> /// <param name="width"></param> /// <param name="length"></param> /// <returns></returns> static NetTile[,] ReadNetTilesFromStream(System.IO.MemoryStream stream, byte width, byte length) { NetTile[,] tiles = new NetTile[width, length]; for (int x = 0; x < width; x++) { for (int y = 0; y < length; y++) { tiles[x, y] = new NetTile(stream); } } return(tiles); }
/// <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, short 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> /// Applies changes to a tile if a tile rect for flower-growing boots is valid /// </summary> /// <param name="realX">The tile x position of the tile rect packet - this is where the flowers are intending to grow</param> /// <param name="realY">The tile y position of the tile rect 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">SendTileRectEventArgs containing event information</param> internal void ProcessFlowerBoots(int realX, int realY, NetTile newTile, GetDataHandlers.SendTileRectEventArgs args) { // We need to get the tile below the tile rect to determine what grass types are allowed if (!WorldGen.InWorld(realX, realY + 1)) { // If the tile below the tile rect 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 rect isn't a valid plant tile (eg grass) then we don't update the server tile state return; } UpdateServerTileState(Main.tile[realX, realY], newTile); }
private static bool HandleSendTileSquare(GetDataHandlerArgs args) { var player = args.Player; var size = args.Data.ReadInt16(); var tileX = args.Data.ReadInt16(); var tileY = args.Data.ReadInt16(); 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; } if (tiles[x, y].Type == Terraria.ID.TileID.LogicSensor && PlotMarker.BlockModify(args.Player, realx, realy)) { args.Player.SendTileSquare(realx, realy, 1); return(true); } } } return(false); }
public static void CheckModifiedTiles(int i, int j, Stream stream) { if (modifiedTile.Where(t => t.i == i && t.j == j).ToArray().Length != 0) { return; } ITile tile = Main.tile[i, j]; stream.Seek(0, SeekOrigin.Begin); var newTile = new NetTile(stream); byte Slope = 0; if (newTile.Slope) { Slope += 1; } if (newTile.Slope2) { Slope += 2; } if (newTile.Slope3) { Slope += 4; } modifiedTile.Add(new TileData() { i = i, j = j, type = tile.type, slope = Slope, active = tile.active(), halfBrick = newTile.IsHalf, wall = tile.wall }); }
/// <summary> /// Iterates over each tile in the tile rectangle 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 IterateTileRect(NetTile[,] tiles, bool[,] processed, GetDataHandlers.SendTileRectEventArgs args) { int tileX = args.TileX; int tileY = args.TileY; byte width = args.Width; byte length = args.Length; for (int x = 0; x < width; x++) { for (int y = 0; y < length; 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 objWidth = data.Width; int objHeight = data.Height; int offsetY = 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 objWidth = 2; objHeight = 3; offsetY = -1; } // Ensure the tile object fits inside the rect before processing it if (!DoesTileObjectFitInTileRect(x, y, objWidth, objHeight, width, length, offsetY, processed)) { continue; } newTiles = new NetTile[objWidth, objHeight]; for (int i = 0; i < objWidth; i++) { for (int j = 0; j < objHeight; j++) { newTiles[i, j] = tiles[x + i, y + j + offsetY]; processed[x + i, y + j + offsetY] = true; } } ProcessTileObject(newTile.Type, realX, realY + offsetY, objWidth, objHeight, newTiles, args); continue; } // If the new tile does not have an associated tile object, process it as an individual tile ProcessSingleTile(realX, realY, newTile, width, length, args); processed[x, y] = true; } } }
/// <summary> /// Updates a single tile's world state with a set of changes from the networked tile state /// </summary> /// <param name="tile">The tile to update</param> /// <param name="newTile">The NetTile containing the change</param> /// <param name="updateType">The type of data to merge into world state</param> public static void UpdateServerTileState(ITile tile, NetTile newTile, TileDataType updateType) { //This logic (updateType & TDT.Tile) != 0 is the way Terraria does it (see: Tile.cs/Clear(TileDataType)) //& is not a typo - we're performing a binary AND test to see if a given flag is set. if ((updateType & TileDataType.Tile) != 0) { tile.active(newTile.Active); tile.type = newTile.Type; if (newTile.FrameImportant) { tile.frameX = newTile.FrameX; tile.frameY = newTile.FrameY; } else if (tile.type != newTile.Type || !tile.active()) { //This is vanilla logic - if the tile changed types (or wasn't active) the frame values might not be valid - so we reset them to -1. tile.frameX = -1; tile.frameY = -1; } } if ((updateType & TileDataType.Wall) != 0) { tile.wall = newTile.Wall; } if ((updateType & TileDataType.TilePaint) != 0) { tile.color(newTile.TileColor); } if ((updateType & TileDataType.WallPaint) != 0) { tile.wallColor(newTile.WallColor); } if ((updateType & TileDataType.Liquid) != 0) { tile.liquid = newTile.Liquid; tile.liquidType(newTile.LiquidType); } if ((updateType & TileDataType.Slope) != 0) { tile.halfBrick(newTile.IsHalf); tile.slope(newTile.Slope); } if ((updateType & TileDataType.Wiring) != 0) { tile.wire(newTile.Wire); tile.wire2(newTile.Wire2); tile.wire3(newTile.Wire3); tile.wire4(newTile.Wire4); } if ((updateType & TileDataType.Actuator) != 0) { tile.actuator(newTile.IsActuator); tile.inActive(newTile.Inactive); } }
private static bool HandleSendTileSquare(GetDataHandlerArgs args) { var size = args.Data.ReadInt16(); var tileX = args.Data.ReadInt32(); var tileY = args.Data.ReadInt32(); if (size > 5) { return(true); } if ((DateTime.UtcNow - args.Player.LastThreat).TotalMilliseconds < 5000) { args.Player.SendTileSquare(tileX, tileY, size); return(true); } if (TShock.CheckIgnores(args.Player)) { args.Player.SendTileSquare(tileX, tileY); return(true); } 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); } } bool changed = false; 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 (TShock.CheckTilePermission(args.Player, x, y)) { continue; } if ((tile.type == 128 && newtile.Type == 128) || (tile.type == 105 && newtile.Type == 105)) { if (TShock.Config.EnableInsecureTileFixes) { return(false); } } if (tile.type == 0x17 && newtile.Type == 0x2) { tile.type = 0x2; changed = true; } else if (tile.type == 0x19 && newtile.Type == 0x1) { tile.type = 0x1; changed = true; } else if ((tile.type == 0xF && newtile.Type == 0xF) || (tile.type == 0x4F && newtile.Type == 0x4F)) { tile.frameX = newtile.FrameX; tile.frameY = newtile.FrameY; changed = true; } // Holy water/Unholy water else if (tile.type == 1 && newtile.Type == 117) { tile.type = 117; changed = true; } else if (tile.type == 1 && newtile.Type == 25) { tile.type = 25; changed = true; } else if (tile.type == 117 && newtile.Type == 25) { tile.type = 25; changed = true; } else if (tile.type == 25 && newtile.Type == 117) { tile.type = 117; changed = true; } else if (tile.type == 2 && newtile.Type == 23) { tile.type = 23; changed = true; } else if (tile.type == 2 && newtile.Type == 109) { tile.type = 109; changed = true; } else if (tile.type == 23 && newtile.Type == 109) { tile.type = 109; changed = true; } else if (tile.type == 109 && newtile.Type == 23) { tile.type = 23; changed = true; } else if (tile.type == 23 && newtile.Type == 109) { tile.type = 109; changed = true; } else if (tile.type == 53 && newtile.Type == 116) { tile.type = 116; changed = true; } else if (tile.type == 53 && newtile.Type == 112) { tile.type = 112; changed = true; } else if (tile.type == 112 && newtile.Type == 116) { tile.type = 116; changed = true; } else if (tile.type == 116 && newtile.Type == 112) { tile.type = 112; changed = true; } else if (tile.type == 112 && newtile.Type == 53) { tile.type = 53; changed = true; } else if (tile.type == 109 && newtile.Type == 2) { tile.type = 2; changed = true; } else if (tile.type == 116 && newtile.Type == 53) { tile.type = 53; changed = true; } else if (tile.type == 117 && newtile.Type == 1) { tile.type = 1; changed = true; } } } if (changed) { TSPlayer.All.SendTileSquare(tileX, tileY, size); WorldGen.RangeFrame(tileX, tileY, tileX + size, tileY + size); } return(true); }
private static bool HandleSendTileSquare(GetDataHandlerArgs args) { short size = args.Data.ReadInt16(); int tilex = args.Data.ReadInt32(); int tiley = args.Data.ReadInt32(); if (size > 5) { return(true); } 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); } } bool changed = false; 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 (tile.type == 0x17 && newtile.Type == 0x2) { tile.type = 0x2; changed = true; } else if (tile.type == 0x19 && newtile.Type == 0x1) { tile.type = 0x1; changed = true; } else if ((tile.type == 0xF && newtile.Type == 0xF) || (tile.type == 0x4F && newtile.Type == 0x4F)) { tile.frameX = newtile.FrameX; tile.frameY = newtile.FrameY; changed = true; } } } if (changed) { TSPlayer.All.SendTileSquare(tilex, tiley, 3); } return(true); }
public override void Read(IStarboundStream stream) { Position = Vec2I.FromStream(stream); NetTile = NetTile.FromStream(stream); //NetTile = stream. }