Wiki 9Minecraft Wiki 9Minecraft

Running Commands On First World Load

How to execute initialization commands exactly once when a world or player is first created.

6Sections
5Code examples
4Known errors

Minecraft Bedrock has no native JSON event that fires strictly once when a world is generated. To set up scoreboard objectives, configure game rules, or hand out starter items without repeating those actions every time the world reopens, you must pair a repeating execution mechanism with a persistent state check. This page covers both the pure command-function approach using scoreboard flags and the modern Script API approach using dynamic properties.

World Load vs First Player Spawn

Before writing your initialization logic, you must distinguish between two very different events: the world itself being initialized for the first time, and an individual player joining the world for the first time. A singleplayer world merges these moments nicely, because the host joins right as the world finishes generating. On a dedicated server or Realm, however, the world initializes once, and twenty different players might join over the course of three weeks.

World-level initialization includes registering dummy scoreboard objectives, setting global game rules like commandblockoutput or doinsomnia, placing a central spawn structure, and establishing global configuration numbers. If these actions run more than once, they can overwrite running scores or rebuild structures on top of player edits.

Player-level initialization includes giving a starter kit, displaying a welcome title, teleporting the newcomer to a tutorial room, and setting initial personal scoreboards. This logic must run once per unique player, rather than once per world. The two patterns look very similar in practice, differing mainly in where the state flag is stored: on an entity tag for the player, or on a fake scoreboard player or world dynamic property for the world.

ScopeWhat Belongs HereWhere State Is Stored
World InitializationCreating scoreboard objectives, setting gamerules, placing spawn structuresFake player score on a scoreboard, or a world Dynamic Property in Script API
Player InitializationStarter items, welcome messages, setting initial personal scores, tutorial teleportsEntity tag on the player (via /tag), or an entity Dynamic Property in Script API
Session Load (Every Reload)Re- validating temporary entities, refreshing UI tickersNot persisted; triggered via playerSpawn event or transient entities

The Pure Command Pattern: tick.json and Scoreboard Flags

If your behaviour pack does not use scripts, your entry point for automatic commands is BP/functions/tick.json. Every function listed in tick.json runs twenty times per second, starting as soon as the world is loaded and ticking. Because tick.json has no concept of 'run once', the function you list inside it must act as a gatekeeper that checks a persistent condition before calling your real setup function.

For world-level setup, the most reliable pure-command gatekeeper is a dummy scoreboard objective paired with a fake player name. Scoreboard objectives and the scores assigned to fake players (strings that do not correspond to real Xbox Live accounts, often prefixed with a hash symbol like #world_init to keep them off the sidebar display) are saved directly into the world database. They survive server restarts, game updates, and players logging in and out.

The trick relies on how the execute if score or scoreboard players test command works. In your ticking function, you first make sure the tracking objective exists, then add zero to your fake player's score. Adding zero to an unset score initializes that score to 0 without altering it if it has already been set to 1. On the next line, you run your initialization function only if that fake player's score matches 0. Inside the initialization function itself, you flip the fake player's score to 1 so the gatekeeper never opens again.

BP/functions/tick.json
{
  "values": [
    "init/check"
  ]
}

Writing the Initialization Functions

Create two function files inside BP/functions/init/: one named check.mcfunction that runs every tick, and one named world_setup.mcfunction that contains your heavy lifting. Keeping them separate ensures Minecraft only parses three lightweight lines of commands per tick once the world is already initialized, rather than evaluating dozens of conditional execute statements twenty times a second.

Notice the order of operations in the code below. In check.mcfunction, we run scoreboard objectives add first. While it is true that running this command every tick when the objective already exists generates an internal failure result, functions listed in tick.json suppress command output by default, so players will not see error spam in chat. If you find the internal check bothersome, the Script API method in the next section avoids ticking checks entirely.

Inside world_setup.mcfunction, we immediately set the #world_loaded score to 1 on the very first line. Putting the lock at the top of the file is a defensive habit: if a command further down the file encounters a syntax error or fails to load a structure because chunks are still generating, you generally do not want the first half of your setup function to repeat endlessly every tick.

BP/functions/init/check.mcfunction
# Ensure the tracking objective exists and initialize the fake player to 0 if unset
scoreboard objectives add wiki_init dummy
scoreboard players add #world_loaded wiki_init 0

# Run the setup function only while the flag is still 0
execute if score #world_loaded wiki_init matches 0 run function init/world_setup

# Also check for any uninitialized players currently in the world
execute as @a[tag=!wiki_joined] at @s run function init/player_setup

Handling the Setup Logic and Player Kits

Now we write the two target functions called by our gatekeeper: world_setup.mcfunction and player_setup.mcfunction. Because player_setup.mcfunction is invoked via execute as @a[tag=!wiki_joined] at @s, the execution context inside that file is already set to the newcomer's entity (@s) and their current coordinates. You can use @s safely for every command in that file.

One critical limitation of world_setup.mcfunction is that when tick.json fires for the very first time, there may not be a player in the world yet, or chunks may not be fully ticked around the world spawn. Commands that modify global state—like gamerule, scoreboard objectives add, or tickingarea add—work without any loaded chunks. Commands that place blocks (setblock, fill, structure load) require the target chunk to be loaded and ticking. If your world setup needs to build a physical lobby, add a ticking area at the target coordinates first and delay the block placement until the ticking area is active, or trigger the build from the first player's spawn instead.

BP/functions/init/world_setup.mcfunction
# Lock this function immediately so it never runs a second time
scoreboard players set #world_loaded wiki_init 1

# Configure global gamerules
gamerule commandblockoutput false
gamerule sendcommandfeedback false
gamerule showcoordinates true

# Create gameplay scoreboards
scoreboard objectives add coins dummy "Coins"
scoreboard objectives add deaths dummy "Deaths"

The Player Initialization Function

The companion file, BP/functions/init/player_setup.mcfunction, handles per-player onboarding. Just as we locked the world scoreboard at the top of the world setup file, we apply the wiki_joined tag on the first line here. Entity tags assigned via /tag are saved to the player's NBT data inside the world database. Even if the player dies, changes dimensions, or logs out for a month, the tag remains on their profile.

When handing out starter items or displaying titles on a player's very first join, keep in mind that the player entity actually spawns in the world slightly before their client finishes the loading screen. Sounds played or titles shown on the exact tick the entity appears are frequently missed because the player is still looking at the 'Generating World' screen. A clean workaround in pure commands is to wait until the player moves or looks around, or simply accept that inventory items and chat messages will be waiting for them when the screen clears.

BP/functions/init/player_setup.mcfunction
# Mark the player as initialized immediately
tag @s add wiki_joined

# Initialize personal scores to 0 so they show up on listings
scoreboard players set @s coins 100
scoreboard players set @s deaths 0

# Give starter gear and a welcome message in chat
give @s apple 5
give @s stone_pickaxe 1
tellraw @s {"rawtext":[{"text":"§aWelcome! You have received your starter kit and 100 Coins."}]}

The Script API Alternative (Zero-Tick Overhead)

If your add-on already uses the @minecraft/server Script API module, you should abandon tick.json for initialization entirely. Running a check twenty times per second for the entire lifespan of a world just to catch a single moment at the very beginning is a waste of resources. The Script API provides dedicated lifecycle events and persistent storage through Dynamic Properties.

In stable @minecraft/server, the world.afterEvents.worldLoad event fires once each time the world is opened and ready for script execution. To distinguish the very first time the world is opened from subsequent reloads, check a boolean or string via world.getDynamicProperty(). Dynamic properties are key-value pairs stored directly on the world or on an entity, invisible to players and unaffected by /scoreboard commands.

Similarly, for players, world.afterEvents.playerSpawn fires whenever a player finishes spawning into the world. This event includes an initialSpawn boolean property on the event data object. Be careful: initialSpawn is true every time a player joins the server session (as opposed to respawning after death), not just the first time they ever join the world. You still need a dynamic property or entity tag on the player to distinguish their first-ever visit from a Tuesday afternoon reconnect.

BP/scripts/main.js
import { world } from "@minecraft/server";

// Fires once per session when the world finishes loading
world.afterEvents.worldLoad.subscribe(() => {
  const isInitialized = world.getDynamicProperty("wiki:world_initialized");
  if (!isInitialized) {
    world.setDynamicProperty("wiki:world_initialized", true);

    const overworld = world.getDimension("overworld");
    overworld.runCommand("gamerule commandblockoutput false");
    overworld.runCommand("scoreboard objectives add coins dummy \"Coins\"");
  }
});

// Fires when a player spawns (joining the game OR respawning from death)
world.afterEvents.playerSpawn.subscribe((event) => {
  // Ignore death respawns; only act when joining the session
  if (!event.initialSpawn) return;

  const player = event.player;
  if (!player.getDynamicProperty("wiki:player_joined")) {
    player.setDynamicProperty("wiki:player_joined", true);

    player.runCommand("give @s apple 5");
    player.sendMessage("§aWelcome to the world! Here are some apples.");
  }
});

Which versions this applies to

The tick.json and mcfunction method works in all modern Bedrock versions (1.19.0+) with no experimental toggles required. Be sure your behaviour pack manifest.json sets min_engine_version to at least [1, 19, 50] so the modern execute syntax (execute if score) is enabled. The Script API method using worldLoad and dynamic properties works on stable @minecraft/server (1.11.0+ module version, Bedrock 1.21.0+) without experimental toggles.

When it does not work

What you seeWhyThe fix
Function [init/check] failed to load: Syntax error: Unexpected "if": at "execute >>if<< score"Your behaviour pack manifest.json has a min_engine_version older than 1.19.50 (for example, [1, 16, 0]), which forces Minecraft to parse your mcfunction files using the legacy execute syntax instead of the new execute if/unless syntax.Open BP/manifest.json and update header.min_engine_version to at least [1, 20, 0] or the current stable game version.
Setup commands run every single tick forever, spamming the player's inventory with items.There is a typo between the tag or scoreboard name checked in check.mcfunction and the one set in world_setup.mcfunction or player_setup.mcfunction, or a syntax error on an earlier line halted the function before the lock command could run.Move the tag @s add or scoreboard players set lock command to the very first line of your setup function, and verify the objective and tag names match character-for-character.
Structure load or setblock commands in world_setup.mcfunction silently fail to place blocks.When tick.json runs on the very first tick of world generation, no players have spawned yet and the target coordinates may be in unloaded chunks.Either trigger physical block placement from the first player's initialization function (where chunks around @s are guaranteed to be loaded), or use /tickingarea add first and wait until the area is loaded before placing blocks.
Welcome title and sound effect do not play when a player creates a new world.The player entity spawns in the server world and triggers @a[tag=!wiki_joined] while the client is still viewing the 'Building terrain' loading screen.Use chat messages (tellraw) instead of titles for instant first-join messages, or delay the title until the player moves off their initial spawn block.

Reference, straight from the game files

Read out of the game itself, so the ids match the version you are building for.