Skip to content

Teleportation

Teleportation is one of the most commonly implemented features in RocketMod administrative plugins. Moving players around the map, teleporting to other players, and teleporting to named locations are standard capabilities in any moderation toolkit. This article covers the complete RocketMod API surface for player teleportation, including the three overloads of UnturnedPlayer.Teleport, position-based teleportation, target-based teleportation, node-based teleportation, vehicle teleportation, cooldown enforcement, and teleport request (TPA) system patterns.

The patterns documented here are drawn from teleportation plugins used on 57 Studios™ Horizon Life RP servers. They have been validated against Unturned 3.x with RocketMod 4.x.

RocketMod plugin teleporting a player in the server console

Prerequisites

  • A working Unturned dedicated server with RocketMod installed. See RocketMod and OpenMod Plugin Basics for setup.
  • Visual Studio 2022 with .NET Framework 4.7.2 targeting.
  • Familiarity with UnturnedPlayer, IRocketCommand, and the RocketMod event model.
  • Basic understanding of Unity's Vector3 and Quaternion types for position and rotation.

What you'll learn

  • How to use the three UnturnedPlayer.Teleport overloads: to another player, to coordinates, and to a node.
  • How to implement a /tp command that teleports to a target player.
  • How to implement a /tppos command that teleports to specific coordinates.
  • How to implement a /tplocation command that teleports to a named node.
  • How to implement a teleport request (TPA) system with accept and deny.
  • How to enforce teleport cooldowns and combat-tag blocking.
  • How to handle vehicle teleportation alongside player teleportation.
  • How to avoid common pitfalls: stale references, position bounds, and rotation defaults.

The Teleport method

RocketMod exposes three overloads of UnturnedPlayer.Teleport for different teleportation scenarios. These overloads cover the vast majority of teleportation use cases in administrative plugins.

Overload 1: Teleport to another player

csharp
public void Teleport(UnturnedPlayer target)

Teleports the calling player to the specified target player's position. The teleported player appears at the target's exact position with the same rotation as the target.

csharp
UnturnedPlayer source = UnturnedPlayer.FromName("Alice");
UnturnedPlayer target = UnturnedPlayer.FromName("Bob");

source.Teleport(target); // Alice teleports to Bob's position

This overload is the simplest teleportation pattern. It is used for /tp commands where a player or admin teleports to another player.

Overload 2: Teleport to coordinates

csharp
public void Teleport(float x, float y, float z)

Teleports the player to the specified world coordinates. The player retains their current rotation. The x, y, and z parameters are world-space coordinates in the Unturned map.

csharp
UnturnedPlayer player = UnturnedPlayer.FromName("Lloyd");

// Teleport to coordinates: x=100, y=10, z=200
player.Teleport(100f, 10f, 200f);

This overload is used for /tppos commands and for teleporting players to fixed locations defined in configuration files.

Overload 3: Teleport to a node

csharp
public void Teleport(PlayerNode node)

Teleports the player to a predefined navigation node on the map. PlayerNode instances are obtained from the map's node system, typically through the LevelNodes or NodeManager API.

csharp
using SDG.Unturned;
using System.Linq;

public static void TeleportToNearestNode(UnturnedPlayer player)
{
    PlayerNode nearestNode = LevelNodes.nodes
        .OfType<PlayerNode>()
        .OrderBy(n => Vector3.Distance(player.Position, n.point))
        .FirstOrDefault();

    if (nearestNode != null)
    {
        player.Teleport(nearestNode);
    }
}

This overload is used for location-based teleportation systems where named points on the map are defined as nodes during map creation.

Implementing teleport commands

/tp command (teleport to player)

csharp
using Rocket.API;
using Rocket.Unturned.Chat;
using Rocket.Unturned.Player;
using System.Collections.Generic;
using UnityEngine;

namespace MyAdminSuite.Commands
{
    public class TpCommand : IRocketCommand
    {
        public string Name => "tp";
        public string Help => "Teleport to a player.";
        public string Syntax => "/tp <player>";
        public List<string> Aliases => new List<string> { "teleport" };
        public List<string> Permissions => new List<string> { "myadminsuite.tp" };
        public AllowedCaller AllowedCaller => AllowedCaller.Player;

        public void Execute(IRocketPlayer caller, string[] command)
        {
            if (command.Length < 1)
            {
                UnturnedChat.Say(caller, "Usage: /tp <player>", Color.red);
                return;
            }

            UnturnedPlayer source = (UnturnedPlayer)caller;
            UnturnedPlayer target = UnturnedPlayer.FromName(command[0]);

            if (target == null)
            {
                UnturnedChat.Say(caller, $"Player '{command[0]}' not found.", Color.red);
                return;
            }

            source.Teleport(target);
            UnturnedChat.Say(caller, $"Teleported to {target.CharacterName}.", Color.green);

            // Teleport effect on arrival
            source.TriggerEffect("5d8c9a03-f1b2-4e4a-9d7c-8b3e6f0a2c15");
        }
    }
}

/tppos command (teleport to coordinates)

csharp
using Rocket.API;
using Rocket.Unturned.Chat;
using Rocket.Unturned.Player;
using System.Collections.Generic;
using UnityEngine;

namespace MyAdminSuite.Commands
{
    public class TpPosCommand : IRocketCommand
    {
        public string Name => "tppos";
        public string Help => "Teleport to specific coordinates.";
        public string Syntax => "/tppos <x> <y> <z>";
        public List<string> Aliases => new List<string>();
        public List<string> Permissions => new List<string> { "myadminsuite.tppos" };
        public AllowedCaller AllowedCaller => AllowedCaller.Player;

        public void Execute(IRocketPlayer caller, string[] command)
        {
            if (command.Length < 3)
            {
                UnturnedChat.Say(caller, "Usage: /tppos <x> <y> <z>", Color.red);
                return;
            }

            if (!float.TryParse(command[0], out float x) ||
                !float.TryParse(command[1], out float y) ||
                !float.TryParse(command[2], out float z))
            {
                UnturnedChat.Say(caller, "Invalid coordinates. Must be numeric floats.", Color.red);
                return;
            }

            UnturnedPlayer player = (UnturnedPlayer)caller;
            player.Teleport(x, y, z);
            UnturnedChat.Say(caller, $"Teleported to ({x:F1}, {y:F1}, {z:F1}).", Color.green);
        }
    }
}

/tplocation command (teleport to a named location)

For location-based teleportation, define a set of named locations in the plugin configuration and teleport to them by name:

csharp
public class TpLocationCommand : IRocketCommand
{
    public string Name => "tplocation";
    public string Help => "Teleport to a named location.";
    public string Syntax => "/tplocation <name>";
    public List<string> Aliases => new List<string> { "tploc" };
    public List<string> Permissions => new List<string> { "myadminsuite.tplocation" };
    public AllowedCaller AllowedCaller => AllowedCaller.Player;

    public void Execute(IRocketPlayer caller, string[] command)
    {
        if (command.Length < 1)
        {
            UnturnedChat.Say(caller, "Usage: /tplocation <name>", Color.red);
            return;
        }

        UnturnedPlayer player = (UnturnedPlayer)caller;
        string locationName = command[0].ToLowerInvariant();

        TeleportLocation location = TpPlugin.Instance.Configuration.Instance.Locations
            .FirstOrDefault(l => l.Name.ToLowerInvariant() == locationName);

        if (location == null)
        {
            UnturnedChat.Say(caller, $"Location '{command[0]}' not found.", Color.red);
            return;
        }

        player.Teleport(location.X, location.Y, location.Z);
        UnturnedChat.Say(caller, $"Teleported to {location.Name}.", Color.green);
    }
}

Location configuration:

csharp
public class TeleportLocation
{
    public string Name;
    public float X;
    public float Y;
    public float Z;
}

Teleport request (TPA) system

A teleport request system allows non-admin players to request teleportation to another player who must accept or deny. This is a standard feature on roleplay servers.

Core TPA logic

csharp
using System;
using System.Collections.Generic;

public class TpaManager
{
    private readonly Dictionary<ulong, PendingRequest> _pendingRequests = new Dictionary<ulong, PendingRequest>();
    private readonly double _timeoutSeconds;

    public TpaManager(double timeoutSeconds)
    {
        _timeoutSeconds = timeoutSeconds;
    }

    public bool SendRequest(UnturnedPlayer requester, UnturnedPlayer target)
    {
        if (_pendingRequests.ContainsKey(target.CSteamID.m_SteamID))
        {
            return false; // Target already has a pending request
        }

        _pendingRequests[target.CSteamID.m_SteamID] = new PendingRequest
        {
            RequesterSteamId = requester.CSteamID.m_SteamID,
            RequesterName = requester.CharacterName,
            Timestamp = DateTime.UtcNow
        };
        return true;
    }

    public bool AcceptRequest(UnturnedPlayer target, out UnturnedPlayer requester)
    {
        requester = null;
        if (!_pendingRequests.TryGetValue(target.CSteamID.m_SteamID, out PendingRequest request))
        {
            return false;
        }

        _pendingRequests.Remove(target.CSteamID.m_SteamID);

        if ((DateTime.UtcNow - request.Timestamp).TotalSeconds > _timeoutSeconds)
        {
            return false; // Request expired
        }

        requester = UnturnedPlayer.FromCSteamID(new Steamworks.CSteamID(request.RequesterSteamId));
        return requester != null && requester.IsConnected;
    }

    public void DenyRequest(UnturnedPlayer target)
    {
        _pendingRequests.Remove(target.CSteamID.m_SteamID);
    }

    private class PendingRequest
    {
        public ulong RequesterSteamId;
        public string RequesterName;
        public DateTime Timestamp;
    }
}

TPA commands

csharp
public class TpaCommand : IRocketCommand
{
    public string Name => "tpa";
    public string Help => "Request to teleport to another player.";
    public string Syntax => "/tpa <player>";
    public List<string> Aliases => new List<string>();
    public List<string> Permissions => new List<string> { "myadminsuite.tpa" };
    public AllowedCaller AllowedCaller => AllowedCaller.Player;

    public void Execute(IRocketPlayer caller, string[] command)
    {
        if (command.Length < 1)
        {
            UnturnedChat.Say(caller, "Usage: /tpa <player>", Color.red);
            return;
        }

        UnturnedPlayer requester = (UnturnedPlayer)caller;
        UnturnedPlayer target = UnturnedPlayer.FromName(command[0]);

        if (target == null)
        {
            UnturnedChat.Say(caller, $"Player '{command[0]}' not found.", Color.red);
            return;
        }

        if (requester.CSteamID == target.CSteamID)
        {
            UnturnedChat.Say(caller, "You cannot request to teleport to yourself.", Color.yellow);
            return;
        }

        if (TpPlugin.Instance.TpaManager.SendRequest(requester, target))
        {
            UnturnedChat.Say(caller, $"Teleport request sent to {target.CharacterName}.", Color.green);
            UnturnedChat.Say(target, $"{requester.CharacterName} wants to teleport to you. Type /tpaccept or /tpdeny.", Color.cyan);
        }
        else
        {
            UnturnedChat.Say(caller, $"{target.CharacterName} already has a pending request.", Color.yellow);
        }
    }
}
csharp
public class TpAcceptCommand : IRocketCommand
{
    public string Name => "tpaccept";
    public string Help => "Accept a teleport request.";
    public string Syntax => "/tpaccept";
    public List<string> Aliases => new List<string>();
    public List<string> Permissions => new List<string> { "myadminsuite.tpaccept" };
    public AllowedCaller AllowedCaller => AllowedCaller.Player;

    public void Execute(IRocketPlayer caller, string[] command)
    {
        UnturnedPlayer target = (UnturnedPlayer)caller;

        if (TpPlugin.Instance.TpaManager.AcceptRequest(target, out UnturnedPlayer requester))
        {
            requester.Teleport(target);
            UnturnedChat.Say(requester, $"Teleported to {target.CharacterName}.", Color.green);
            UnturnedChat.Say(target, $"{requester.CharacterName} has teleported to you.", Color.green);
        }
        else
        {
            UnturnedChat.Say(caller, "No pending teleport request found.", Color.yellow);
        }
    }
}

Combat-tag blocking

On roleplay servers, teleportation should be blocked during combat to prevent combat logging. The following pattern checks a combat-tag state before allowing teleportation:

csharp
public static class CombatTag
{
    private static readonly Dictionary<ulong, DateTime> _combatTags = new Dictionary<ulong, DateTime>();

    public static void Tag(UnturnedPlayer player, double durationSeconds = 30.0)
    {
        _combatTags[player.CSteamID.m_SteamID] = DateTime.UtcNow.AddSeconds(durationSeconds);
    }

    public static bool IsTagged(UnturnedPlayer player)
    {
        if (_combatTags.TryGetValue(player.CSteamID.m_SteamID, out DateTime expiry))
        {
            if (DateTime.UtcNow < expiry)
                return true;
            _combatTags.Remove(player.CSteamID.m_SteamID);
        }
        return false;
    }

    public static void Clear(UnturnedPlayer player)
    {
        _combatTags.Remove(player.CSteamID.m_SteamID);
    }
}

Integrate combat-tag checking into the teleport command:

csharp
if (CombatTag.IsTagged(player))
{
    UnturnedChat.Say(caller, "You are in combat and cannot teleport.", Color.red);
    return;
}

Teleport cooldown enforcement

Teleport commands should have a per-player cooldown to prevent spam. Use the same CooldownTracker pattern from the earlier articles:

csharp
if (TpPlugin.Instance.TpCooldown.IsOnCooldown(caller.Id))
{
    double remaining = TpPlugin.Instance.TpCooldown.Remaining(caller.Id);
    UnturnedChat.Say(caller, $"Teleport is on cooldown. Wait {remaining:F1}s.", Color.yellow);
    return;
}
TpPlugin.Instance.TpCooldown.SetUsed(caller.Id);

Teleport safety checks

Before teleporting, validate that the destination is safe — not inside a structure, not underground, and not in a restricted zone:

csharp
public static bool IsSafeTeleportPosition(Vector3 position)
{
    // Check for ground below the position
    if (Physics.Raycast(position + Vector3.up * 2f, Vector3.down, out RaycastHit hit, 10f, RayMasks.GROUND))
    {
        // Position is above ground
        return true;
    }
    return false; // No ground found — position is in the void
}

public static Vector3 FindSafePosition(Vector3 desiredPosition)
{
    // Try the desired position first
    if (IsSafeTeleportPosition(desiredPosition))
        return desiredPosition;

    // Search upward for a safe position
    for (float yOffset = 1f; yOffset <= 10f; yOffset += 1f)
    {
        Vector3 testPos = desiredPosition + Vector3.up * yOffset;
        if (IsSafeTeleportPosition(testPos))
            return testPos;
    }

    // Fallback to the top of the nearest structure
    return desiredPosition + Vector3.up * 10f;
}

Full teleport plugin structure

csharp
using Rocket.Core.Plugins;
using Rocket.Unturned.Events;
using Rocket.Unturned.Player;
using System;
using System.Collections.Generic;

namespace TpPlugin
{
    public class TpPlugin : RocketPlugin<TpPluginConfiguration>
    {
        public static TpPlugin Instance { get; private set; }
        public TpaManager TpaManager { get; private set; }
        public CooldownTracker TpCooldown { get; private set; }

        protected override void Load()
        {
            Instance = this;
            TpaManager = new TpaManager(Configuration.Instance.TpaTimeoutSeconds);
            TpCooldown = new CooldownTracker(Configuration.Instance.TpCooldownSeconds);
            UnturnedPlayerEvents.OnPlayerConnected += OnPlayerConnected;
            Logger.Log("[TpPlugin] Loaded");
        }

        protected override void Unload()
        {
            UnturnedPlayerEvents.OnPlayerConnected -= OnPlayerConnected;
            Instance = null;
            Logger.Log("[TpPlugin] Unloaded");
        }

        private void OnPlayerConnected(UnturnedPlayer player)
        {
            // Clear any stale combat tag on reconnect
            CombatTag.Clear(player);
        }
    }

    public class TpPluginConfiguration : IRocketPluginConfiguration
    {
        public double TpCooldownSeconds;
        public double TpaTimeoutSeconds;
        public List<TeleportLocation> Locations;

        public void LoadDefaults()
        {
            TpCooldownSeconds = 5.0;
            TpaTimeoutSeconds = 30.0;
            Locations = new List<TeleportLocation>
            {
                new TeleportLocation { Name = "Spawn", X = 0f, Y = 5f, Z = 0f },
                new TeleportLocation { Name = "Market", X = 150f, Y = 8f, Z = -200f },
            };
        }
    }
}

Common errors and diagnostics

SymptomCauseResolution
'UnturnedPlayer' does not contain a definition for 'Teleport' with 3 float argumentsThe Teleport method does not accept individual float parameters — it takes a Vector3Use player.Teleport(new Vector3(x, y, z)) instead of player.Teleport(x, y, z)
Player teleports but falls through the worldTeleport position is in the void, below the terrainUse FindSafePosition to validate and adjust the destination
Player teleports but direction is wrongRotation not preserved or setWhen teleporting to coordinates, set rotation explicitly after teleport: player.Player.look.yaw = targetYaw
TPA request accepted but teleport failsRequester disconnected during the timeout windowCheck requester.IsConnected before executing the teleport
Teleport cooldown resets on server restartCooldown state is in-memory onlyPersist cooldown state to disk if it must survive restarts
/tppos command throws FormatExceptionNon-numeric input passed for coordinatesValidate all three coordinate parameters before calling Teleport
Player teleports into a structure and gets stuckNo collision check at destinationAdd IsSafeTeleportPosition check before teleporting
Teleport to player works but both players appear at wrong heightTarget player is on a slope or structureThe target's position includes their exact y value; this is correct behavior for accurate teleportation
Console teleport NullReferenceExceptionAllowedCaller.Both with caller as UnturnedPlayer being nullRestrict to AllowedCaller.Player or add console-specific handling

Frequently asked questions

How do I find the coordinates of my current position?

Use the UnturnedPlayer.Position property while in-game. The position returns a Vector3 with x, y, and z components. Some RocketMod plugins implement a /pos or /whereami command that echoes the player's position to chat.

Can I teleport a player to a location on a different map?

No. Teleportation is map-local. You cannot teleport a player to a different map through RocketMod's Teleport method. Map transitions require the player to reconnect to a different server or use Unturned's built-in map-change mechanism.

How do I teleport all players to a single location?

Iterate over all connected players and call Teleport on each:

csharp
foreach (var pair in Provider.clients)
{
    UnturnedPlayer player = UnturnedPlayer.FromSteamPlayer(pair);
    player.Teleport(targetPosition);
}

Does Teleport work on players in vehicles?

Yes. When a player is in a vehicle and Teleport is called, both the player and the vehicle are teleported to the destination. The player remains in their current seat.

How do I teleport a player and set their facing direction?

After calling Teleport, set the player's yaw (horizontal rotation):

csharp
player.Teleport(target);
player.Player.look.yaw = 180f; // Face south

The look.yaw value is in degrees. 0 = north, 90 = east, 180 = south, 270 = west.

Can I prevent teleportation in certain zones?

Yes. Check if the destination or origin is within a restricted zone before allowing the teleport. Zone definitions can be stored in the plugin configuration as position-radius pairs.

Cross-references

Document history

VersionDateAuthorNotes
1.02025-07-2757 StudiosInitial publication. Teleport overloads, TPA system, combat-tag blocking, cooldown enforcement, safety checks, and diagnostics.