PvP Arena System
A complete PvP arena with spawns, boundaries, and combat tracking.
A PvP arena is an isolated space where players can fight without affecting the main world. The arena enforces boundaries so players can't escape, tracks kills and deaths, and manages respawning. This is essential for competitive gameplay.
How It Works
The system works in phases:
- Waiting: Players join and wait in a waiting area
- Setup: When 2 players join, they're teleported to opposite spawns
- Combat: Players fight. If they leave the boundary, they're forced back
- End: When one player quits or time expires, the match ends
We use a Set to track active players (fast membership checks) and a Map to store their stats (kills, deaths).
Arena Setup
First, we define the arena's physical layout:
import { world } from "@minecraft/server";
const ARENA = {
spawn1: { x: 0, y: 65, z: 0 }, // Where player 1 spawns
spawn2: { x: 100, y: 65, z: 0 }, // Where player 2 spawns
boundary: {
min: { x: -50, y: 64, z: -50 }, // Minimum allowed position
max: { x: 150, y: 120, z: 50 } // Maximum allowed position
},
waitingArea: { x: -100, y: 65, z: 0 } // Where players wait
};
let arenaPlayers = new Map(); // Stores kill/death stats
let inArena = new Set(); // Just tracks who's fighting
Why separate the Set and Map? Sets are faster for "is X in here?" checks. Maps are good for storing multiple values per key. We use both for performance.
Join Arena
function joinArena(player) {
if (inArena.size >= 2) {
player.sendMessage("Arena is full!");
return;
}
inArena.add(player.nameTag);
arenaPlayers.set(player.nameTag, {
kills: 0,
deaths: 0,
joinTime: Date.now()
});
// Teleport to spawn
const spawn = inArena.size === 1 ? ARENA.spawn1 : ARENA.spawn2;
player.teleport(spawn);
player.sendMessage("Welcome to the arena!");
if (inArena.size === 2) {
startArena();
}
}
function leaveArena(player) {
inArena.delete(player.nameTag);
arenaPlayers.delete(player.nameTag);
player.teleport(ARENA.waitingArea);
}
Enforce Boundaries
import { world, system } from "@minecraft/server";
system.runInterval(() => {
world.getAllPlayers().forEach((player) => {
if (!inArena.has(player.nameTag)) return;
const pos = player.location;
const b = ARENA.boundary;
// Check boundaries
if (pos.x < b.min.x || pos.x > b.max.x ||
pos.y < b.min.y || pos.y > b.max.y ||
pos.z < b.min.z || pos.z > b.max.z) {
const players = Array.from(inArena);
const spawn = player.nameTag === players[0] ? ARENA.spawn1 : ARENA.spawn2;
player.teleport(spawn);
player.sendMessage("You left the arena!");
}
});
}, 1);
Track Kills and Deaths
world.afterEvents.entityDie.subscribe((event) => {
const entity = event.deadEntity;
if (entity.typeId !== "minecraft:player") return;
const player = entity;
const damageSource = event.damageSource.damagingEntity;
if (!inArena.has(player.nameTag)) return;
const stats = arenaPlayers.get(player.nameTag);
stats.deaths += 1;
// Award kill
if (damageSource?.typeId === "minecraft:player") {
const killerStats = arenaPlayers.get(damageSource.nameTag);
killerStats.kills += 1;
damageSource.sendMessage(`You killed ${player.nameTag}!`);
}
// Respawn
const spawn = player.nameTag ===
Array.from(inArena)[0] ? ARENA.spawn1 : ARENA.spawn2;
// Wait then respawn
setTimeout(() => {
player.teleport(spawn);
}, 3000);
});
Arena Commands
world.beforeEvents.chatSend.subscribe((event) => {
const player = event.sender;
const message = event.message;
if (message === "!join") {
event.cancel = true;
joinArena(player);
}
if (message === "!leave") {
event.cancel = true;
leaveArena(player);
}
if (message === "!stats") {
event.cancel = true;
if (!inArena.has(player.nameTag)) {
player.sendMessage("You're not in the arena!");
return;
}
const stats = arenaPlayers.get(player.nameTag);
player.sendMessage(`Kills: ${stats.kills}`);
player.sendMessage(`Deaths: ${stats.deaths}`);
player.sendMessage(`K/D Ratio: ${(stats.kills / Math.max(1, stats.deaths)).toFixed(2)}`);
}
});
End Arena Match
function endArena() {
const players = Array.from(inArena);
if (players.length < 2) {
return;
}
// Get winner
const winner = players.reduce((a, b) => {
const statsA = arenaPlayers.get(a);
const statsB = arenaPlayers.get(b);
return statsA.kills > statsB.kills ? a : b;
});
const winnerStats = arenaPlayers.get(winner);
world.getAllPlayers().forEach(p => {
if (inArena.has(p.nameTag)) {
p.sendMessage(`Winner: ${winner} with ${winnerStats.kills} kills!`);
leaveArena(p);
}
});
inArena.clear();
arenaPlayers.clear();
}
// Auto-end after 10 minutes
let arenaTimer = null;
function startArena() {
arenaTimer = setTimeout(() => {
endArena();
}, 600000);
}