5 min read

Minecraft Command Teleportation System with Scoreboard

Adapted from an original note I created on April 19, 2022, at 8:44 PM UTC+8, last updated on April 20, 2022, at 9:52 PM UTC+8

This system was initially created for use in a survival server hosted on (the platform) Netease Minecraft--the Chinese version of Minecraft. (I was one of the admins of the server, and was responsible for mostly technical tasks.) Because the server is vanilla (version 1.12.2) and thus does not support installing Spigot plugins, a command-based solution was needed for the player teleportation feature.

Some original requirements are that this system would work without manual intervention from operators (admins), players would be able to initiate the teleportation by themselves, and most importantly, it would work by using only native features such as scoreboards and triggers.

🔠
Throughout this post, a "teleporter" is a player who wants to be teleported to the "teleportee."

Before this system was created, the approach used by most servers involves a teleporte using /trigger to broadcast a tellraw message that includes a clickable link containing a command for a teleporter to directly /tp to the teleportee. However, this implementation poses a significant safety risk as anyone (including bad players) could click and teleport to the teleportee--a thing the teleportee probably not intended.

Therefore, I built this system. This system is based solely on scoreboards and triggers and allows players to use it with or without requiring a teleporter to send a request at first, which is better than any teleportation system that had appeared back then.

Implementation Details

Names of all scoreboards appearing below: tp (trigger), tp_code, tp_check (temporary), tp_id, tp_accept (trigger), tp_id_check (temporary)

Custom "Commands:" /trigger tp <code>, /trigger tp_accept <request ID>

Either with or without "tp requests," each player has their private "tp code," and could share the code with anyone who wants to teleport to the player.

Without Tp Requests

In this mode, the teleporter is directly teleported to the teleportee after using the "tp code."

How It Works

A player (teleporter) who wants to be teleported will use /trigger to set their score on the scoreboard tp to be the "tp code" of a teleportee. Chained repeating command blocks will first copy the scoreboard tp_code to a temporary scoreboard tp_check, and then deduct @r's tp from @a's tp_check. @r will "iterate" through all players for each execution of the command block chain. After this operation, if a player has a tp_check score of 0, they have the exact "tp code" that the teleporter @r entered. Thus, the teleportation could be made.

Example Code

scoreboard players random @a tp_code 100000 999999

Generate a "tp code" for all players (potential teleportees). You can allow a player to reset this "tp code" (e.g. if the code is leaked).

⚠️
Every command in this post that uses scoreboard players random intends to generate a unique value.
Teleporter: /trigger tp set 123456

Teleporter uses the "tp code" given by another player. The code is saved to the scoreboard tp.

First Repeating Command Block Chain

(Iterating each player using @r)

scoreboard players operation @a tp_check = @a tp_code

scoreboard players operation @a tp_check -= @r tp

Now, @r is the teleporter of the teleportee @p[score_tp_check_min=0,score_tp_check=0].

tp @r @p[score_tp_check_min=0,score_tp_check=0]

tellraw @r ...(success message)

With Tp Requests

In addition to the existing implementation, the teleportee will also review (choose to accept or deny) a "tp request" from a teleporter who has used the "tp code." A teleporter is only teleported if their request is accepted by the teleportee.

How It Works

Instead of directly teleporting the teleporter, a random score is given to the teleporter on the scoreboard tp_id, and the teleportee is notified of this tp request.

⚠️
tp_id must be kept secret because anyone could use it to become the teleportee of a teleporter. In this implementation, we would only send it to the intended teleportee.
💡
Resetting tp_id means canceling the tp request, which is what you could allow the teleporter to or regularly clear the value. (Otherwise, this value will also change when the teleporter decide to send a request to another teleportee.)

When the teleportee uses /trigger with the tp_id to accept the request, a technique similar to how we filtered the matching teleportee in the previous section will be used to filter the teleporter with the tp_id, and then the teleporter can be teleported to the teleportee.

Example Code

First Repeating Command Block Chain

(Continued)

execute @p[score_tp_check_min=0,score_tp_check=0] ~ ~ ~ scoreboard players random @r tp_id 1000 9999

We can then send the request message to the teleportee.

tellraw @p[score_tp_check_min=0,score_tp_check=0] ...(request received message)

Teleportee: /trigger tp_accept set 4321

Teleportee accepts a "tp request." The request ID is saved to tp_accept.

Second Repeating Command Block Chain

(Iterating each player using @r)

scoreboard players operation @a tp_id_check = @a tp_id

scoreboard players operation @a tp_id_check -= @r tp_accept

Now, the teleporter @p[score_tp_id_check_min=0,score_tp_id_check=0] is being accepted by the teleportee @r.

tp @p[score_tp_id_check_min=0,score_tp_id_check=0] @r

tellraw @p[score_tp_id_check_min=0,score_tp_id_check=0] ...(success message)


Potential Improvements

  • There could be a confirmation for the teleporter before they are teleported to the teleportee, but I haven't figured out how to implement this feature.
    • Update: Implemented in the Complete Demos section.
  • There still exists the safety threat of the teleportee sending the request ID to a malicous actor that could unintendedly become the teleportee.
    • Update: Fixed in the Complete Demos section.

Complete Demos

The following are two complete demos. Both combine the two modes above and include support for a player to choose their preferred mode. The Enhanced Version demonstrates the implementation of even more features.

Blog Version

Names of all scoreboards appearing below: tp (trigger), tp_code, tp_check (temporary), tp_id, tp_accept (trigger), tp_id_check (temporary), conf_tpCodeAuto (trigger), conf_tpRequests (trigger)

Custom "Commands:" /trigger tp <code>, /trigger tp_accept <request ID>, /trigger conf_tpCodeAuto <1|0>, /trigger conf_tpRequests <1|0>

Completes Teleportation Without Requests & Proceeds Part 1 of Teleportation With Requests

# `@r` is teleporter and `@p[score_tp_check_min=0,score_tp_check=0]` is teleportee:
scoreboard players operation @a tp_check = @a tp_code
scoreboard players operation @a tp_check -= @r tp
# Failed:
execute @p[score_tp_check_min=0,score_tp_check=0] ~~~ scoreboard players reset @r tp
tellraw @r[score_tp_min=1,score_tp=99999] (invalid tp code message)
tellraw @r[score_tp_min=100000,score_tp=999999] (player not found message)
tellraw @r[score_tp_min=1000000] (invalid tp code message)
scoreboard players reset @r tp
# `conf_tpRequests` is disabled:
tp @r @p[score_tp_check_min=0,score_tp_check=0,score_conf_tpRequests_min=0,score_conf_tpRequests=0]
execute @p[score_tp_check_min=0,score_tp_check=0,score_conf_tpRequests_min=0,score_conf_tpRequests=0] ~~~ tellraw @r (success message)
tellraw @p[score_tp_check_min=0,score_tp_check=0,score_conf_tpRequests_min=0,score_conf_tpRequests=0] (success message)
# `conf_tpRequests` is enabled:
execute @p[score_tp_check_min=0,score_tp_check=0,score_conf_tpRequests_min=1,score_conf_tpRequests=1] ~~~ scoreboard players random @r tp_id 1000 9999
execute @p[score_tp_check_min=0,score_tp_check=0,score_conf_tpRequests_min=1,score_conf_tpRequests=1] ~~~ tellraw @r (request sent message)
tellraw @p[score_tp_check_min=0,score_tp_check=0,score_conf_tpRequests_min=1,score_conf_tpRequests=1] (request received message)
# `conf_tpCodeAuto` is enabled:
scoreboard players random @p[score_tp_check_min=0,score_tp_check=0,score_conf_tpCodeAuto_min=0,score_conf_tpCodeAuto=0] tp_code 100000 999999
tellraw @p[score_tp_check_min=0,score_tp_check=0,score_conf_tpCodeAuto_min=0,score_conf_tpCodeAuto=0] (code auto refreshed message)

Repeating Command Block Chain

Completes Part 2 of Teleportation With Requests

# `@p[score_tp_id_check_min=0,score_tp_id_check=0]` is teleporter and `@r` is teleportee:
scoreboard players operation @a tp_id_check = @a tp_id
scoreboard players operation @a tp_id_check -= @r tp_accept
# Failed:
execute @p[score_tp_id_check_min=0,score_tp_id_check=0] ~~~ scoreboard players reset @r tp_accept
tellraw @r[score_tp_accept_min=1,score_tp_accept=9999] (invalid tp code message)
tellraw @r[score_tp_accept_min=10000,score_tp_accept=99999] (player not found message)
tellraw @r[score_tp_accept_min=100000] (invalid tp code message)
scoreboard players reset @r tp_accept
# Success:
tp @p[score_tp_id_check_min=0,score_tp_id_check=0] @r
tellraw @p[score_tp_id_check_min=0,score_tp_id_check=0] (success message)
execute @p[score_tp_id_check_min=0,score_tp_id_check=0] ~~~ tellraw @r (success message)

Repeating Command Block Chain

Request Expiration System

scoreboard players add @a[score_tp_id_min=1] tp_id_timeout 1
# `@a[score_tp_id_min=1,score_tp_id_timeout_min=1200]` is teleporter with an expired request:
#                                               ^ 20 ticks = 1 second, 20 * 60 = 1200 ticks = 1 minute
tellraw @a[score_tp_id_min=1,score_tp_id_timeout_min=1200] (request expired message)
scoreboard players reset @a[score_tp_id_min=1,score_tp_id_timeout_min=1200] tp_id
scoreboard players reset @a[score_tp_id_timeout_min=1200] tp_id_timeout

Repeating Command Block Chain

Enhanced Version With tp_target in Addition to tp_id

Names of all scoreboards appearing below: tp (trigger), tp_code, tp_check (temporary), tp_id, tp_accept (trigger), tp_id_check (temporary), conf_tpCodeAuto (trigger), conf_tpRequests (trigger), tp_target

Custom "Commands:" /trigger tp <code>, /trigger tp_accept <request ID>, /trigger conf_tpCodeAuto <1|0>, /trigger conf_tpRequests <1|0>

Completes Teleportation Without Requests & Proceeds Part 1 of Teleportation With Requests