Skip to content

en Basic TP Entity

HoCha113 edited this page Jun 21, 2026 · 8 revisions

中文版本

What is TileProcessor?

TileProcessor is an entity similar to TileEntity that attaches to tiles and performs independent logical updates and drawing updates, used for creating various advanced tile effects.

The differences from TileEntity:

  • More independent: TileProcessor is a completely independent entity system — no need to write any special logic in Tile code
  • Auto-mounting: Just specify TargetTileID, and the system automatically creates TP entities for all matching tiles in the world
  • Built-in network sync: Provides a complete multiplayer data synchronization solution
  • Persistent storage: Supports SaveData/LoadData for world save data persistence
  • Multi-layer drawing: Provides four draw layers: PreTileDraw, BackDraw, Draw, FrontDraw
  • Performance optimization: Supports IdleDistance to automatically pause updates for distant entities

Key Properties

Identity & Configuration

Property/Field Type Default Description
TargetTileID int -1 Target tile type ID, -1 means no auto-mounting
ID int Globally unique type ID (read-only, assigned at load)
WhoAmI int Local index (may differ per client)
Mod Mod Owning mod instance
FullName string Internal full name

State

Property/Field Type Default Description
Active bool Whether active, stops updating and drawing when false
Spwan bool Whether Initialize() has been called
HoverTP bool Whether the mouse is hovering over the entity
InScreen bool Whether on screen (updated in real-time during drawing)
Tile Tile default The followed tile (updated once on load/place)
TrackItem Item null Item used for placement, null when spawned during world init

Position & Size

Property Type Description
Position Point16 Tile coordinates
PosInWorld Vector2 World pixel coordinates (read-only)
CenterInWorld Vector2 World center pixel coordinates (read-only)
Size Vector2 Size in pixels (read-only)
HitBox Rectangle Collision rectangle (read-only)
Width int Width in pixels (auto-fetched from TileObjectData)
Height int Height in pixels (auto-fetched from TileObjectData)

Rendering & Performance

Property/Field Type Default Description
DrawExtendMode int 160 Draw extension range (pixels beyond screen still drawn)
IdleDistance int -1 Idle distance (pixels), pauses updates beyond this, -1 disables

Network

Property/Field Type Default Description
LoadenWorldSendData bool true Request network data when entering world
PlaceNet bool false Broadcast network data when placed
SendCooldownTicks int 0 Send cooldown (>0 prevents sending)
SendpacketCount int 0 Packets sent this second (resets every 60 ticks)
SendpacketPeak int 10 Max packets per second

Inheritance and Usage of TileProcessor

The best way to understand a class is to read example code directly. Here is a TileProcessor example from InnoVaultExample

// We recommend adding the TP suffix to any TileProcessor subclass
// This TP entity will be associated with the vanilla Wooden Workbench
// Unlike TileEntity, we don't need to write any code in the Tile — TileProcessor is quite independent
internal class ExampleWorkbenchTP : TileProcessor
{
    // Override this property to point to TileID.WorkBenches (vanilla workbench tile ID)
    // This will auto-generate this TP entity when the player places a workbench
    // All existing workbenches in the world will also get this TP entity automatically
    public override int TargetTileID => TileID.WorkBenches;

    private Vector2 Center => PosInWorld + new Vector2(16, 8);
    private Color tpColor = Color.White;
    private float light;

    public override void SetProperty() {
        tpColor = new Color(Main.rand.NextFloat(), Main.rand.NextFloat(), Main.rand.NextFloat());
        light = 1 + Main.rand.NextFloat();
    }

    // This update function runs on all clients and server, called every frame
    public override void Update() {
        Lighting.AddLight(Center, tpColor.ToVector3() * light);
    }

    // This draw function lets you draw something interesting on the tile
    public override void Draw(SpriteBatch spriteBatch) {
        int itemType = (Main.mouseItem.IsAir ? Main.LocalPlayer.inventory[Main.LocalPlayer.selectedItem] : Main.mouseItem).type;
        Vector2 orig = default;
        if (itemType > ItemID.None) {
            Main.instance.LoadItem(itemType);
            Texture2D texture = TextureAssets.Item[itemType].Value;
            Rectangle? rectangle = Main.itemAnimations[itemType] != null 
                ? Main.itemAnimations[itemType].GetFrame(texture) : texture.Frame(1, 1, 0, 0);
            orig = texture.Size() / 2;
            if (rectangle.HasValue) {
                orig = rectangle.Value.Size() / 2;
            }
            float gfkY = -16 - rectangle.Value.Height / 2 + MathF.Sin(Main.GameUpdateCount * 0.1f) * 4;
            Vector2 drawPos = Center + new Vector2(0, gfkY);
            drawPos -= Main.screenPosition;
            spriteBatch.Draw(texture, drawPos, rectangle, Color.White, 0, orig, 1, SpriteEffects.None, 0);
        }
    }
}

As mentioned in the comments, it's recommended to add the TP suffix to any TileProcessor entity class for easier identification.

public override int TargetTileID => TileID.WorkBenches;

This example creates a TileProcessor entity attached to TileID.WorkBenches. This means every workbench tile in the world will have a dedicated entity attached.

Note: TargetTileID should not return 0, because 0 is the tile ID for Dirt and is also the return value when ModContent.TileType<T>() fails.


Complete Lifecycle

Initialization Phase

Method When Called Environment Description
LoadenTile() When entity spawns All sides Loads tile data, auto-sets Width/Height
SetProperty() When entity spawns All sides Initialize instance data
Initialize() When added to world (once) All sides Initialize secondary data, basic data is already set

Per-Frame Update

Method When Called Environment Description
Update() Every frame All clients and server Main logic update
SingleInstanceUpdate() Once per update cycle All sides Called only once per type (requires instances in world)
IsDaed() Every frame Server/singleplayer only Returns true to mark entity as dead

Interaction

Method When Called Environment Description
RightClick(i, j, tile, player) When tile is right-clicked All sides Return null to continue vanilla events, otherwise block
HoverTile() When mouse hovers Local client only Set cursor icons, etc.

Drawing (Client Only)

Method Draw Layer Description
PreTileDraw(SpriteBatch) Before tile drawing Bottom-most layer
BackDraw(SpriteBatch) Below Draw Background layer
Draw(SpriteBatch) Main draw Primary draw layer
FrontDraw(SpriteBatch) Above Draw Foreground layer

World Events

Method When Called Environment Description
LoadInWorld() When world loads All sides Save data not loaded on client
UnLoadInWorld() When world unloads Server/singleplayer only Cleanup
ClientSaveAndQuit() Save and quit Client only

Death

Method Description
Kill() Marks dead and triggers OnKill(). Not recommended to call directly — should be triggered by destroying the tile
CheckDeath() Check and properly handle death (recommended, with network adaptation)
OnKill() Death event callback
KillMultiTileSet(frameX, frameY) Callback when a multi-tile is mined

Network Synchronization

The TP system has built-in multiplayer network synchronization:

internal class MyStorageTP : TileProcessor
{
    public override int TargetTileID => ModContent.TileType<MyStorageTile>();
    
    private int storedValue;
    
    public MyStorageTP() {
        // Request data from server when entering world
        LoadenWorldSendData = true;
        // Broadcast data to other clients when placed
        PlaceNet = true;
    }

    // Send data to clients
    public override void SendData(ModPacket data) {
        data.Write(storedValue);
    }
    
    // Receive data from server
    public override void ReceiveData(BinaryReader reader, int whoAmI) {
        storedValue = reader.ReadInt32();
    }
    
    // Actively trigger send (e.g., after player interaction)
    public override bool? RightClick(int i, int j, Tile tile, Player player) {
        storedValue++;
        SendData(); // Call the parameterless version to trigger network send
        return true;
    }
}

Network Methods

Method Description
SendData() Trigger network send (subject to cooldown and peak limits)
SendData(ModPacket) Write data to send. Also called by server to sync state when new clients join
ReceiveData(BinaryReader, int) Receive data. Must match SendData(ModPacket) read/write order
NetCloneSend(ModPacket) Network clone send (basic fields like Active, WhoAmI, Position)
NetCloneRead(BinaryReader) Network clone receive

Important: SaveData is only called on the server side. To get persistent data on the client, send it via SendData(ModPacket).


Persistent Storage

internal class MyPersistentTP : TileProcessor
{
    public override int TargetTileID => ModContent.TileType<MyTile>();
    
    private int savedCounter;
    
    // Server-side only
    public override void SaveData(TagCompound tag) {
        tag["counter"] = savedCounter;
    }
    
    // Server-side only
    public override void LoadData(TagCompound tag) {
        savedCounter = tag.GetInt("counter");
    }
    
    // Sync save data to clients via network
    public override void SendData(ModPacket data) {
        data.Write(savedCounter);
    }
    
    public override void ReceiveData(BinaryReader reader, int whoAmI) {
        savedCounter = reader.ReadInt32();
    }
}

SyncVar Automatic Synchronization

For simple field synchronization, use the [SyncVar] attribute with SyncVarManager to simplify network code:

internal class MySyncTP : TileProcessor
{
    [SyncVar] private int health;
    [SyncVar] private float speed;
    [SyncVar] private bool isActive;
    
    public override void SendData(ModPacket data) {
        SyncVarManager.Send(this, data);
    }
    
    public override void ReceiveData(BinaryReader reader, int whoAmI) {
        SyncVarManager.Receive(this, reader);
    }
}

SyncVarManager supported types: int, float, bool, string, byte, short, long, double, Color, Vector2, Point16, Point, Item.


GlobalTileProcessor (Global TP Hooks)

Inherit GlobalTileProcessor to modify the behavior of all TP entities:

public class MyGlobalTP : GlobalTileProcessor
{
    // When entity initializes
    public override void Initialize(TileProcessor tp) { }
    
    // Before update, return false to prevent this entity from updating
    public override bool PreUpdate(TileProcessor tp) => true;
    
    // After update
    public override void PostUpdate(TileProcessor tp) { }
    
    // Before single-instance update (once per type per frame)
    public override bool PreSingleInstanceUpdate(TileProcessor tp) => true;
    
    // Before/after draw
    public override bool PreDraw(TileProcessor tp, SpriteBatch sb) => true;
    public override void PostDraw(TileProcessor tp, SpriteBatch sb) { }
    
    // Before/after global draw (all instances)
    public override bool PreDrawEverything(SpriteBatch sb) => true;
    public override void PostDrawEverything(SpriteBatch sb) { }
    
    // Death check override (return null to not affect original check)
    public override bool? IsDaed(TileProcessor tp) => null;
    
    // Death event
    public override void OnKill(TileProcessor tp) { }
    
    // Custom multi-structure tile origin point lookup
    public override Point16? GetTopLeftPoint(int x, int y) => null;
    public override bool? TryIsTopLeftPoint(int x, int y, out Point16 position) {
        position = default;
        return null;
    }
}

TileProcessorLoader Common Methods

Method/Property Description
TileProcessorLoader.TP_InWorld List of all active TP instances in the world
TileProcessorLoader.ByPositionGetTP(i, j, out tp) Get TP instance by tile coordinates
TileProcessorLoader.AddInWorld(ID, pos, trackItem) Manually add a TP instance to the world
TileProcessorLoader.MaxTPInWorldCount Max TP count in world (32767)
TileProcessorLoader.WorldLoadProgress World load progress (0-100)

Finding TP Instances via VaultContent

TileProcessor tp = Mod.FindTP<ExampleWorkbenchTP>();
TileProcessor tp2 = Mod.FindTP("ExampleWorkbenchTP");

Navigation

Previous Home Next
RenderHandle Pipeline Home Basic UI

Clone this wiki locally