fix(easter-eggs): place eggs around player location, validate before skipping
All checks were successful
Deploy Addons / deploy (push) Successful in 15s

- Use joining player's position as egg centre instead of world default
  spawn, so eggs appear where players actually play
- Store centre in dynamic property so all players find the same eggs
- Validate that egg blocks actually exist before trusting eggs_placed flag,
  auto-recovering if setblock commands silently failed (e.g. unloaded chunks)
- Trigger placement on first playerSpawn not server startup timeout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-05 02:29:10 +01:00
parent b91aec3bf7
commit 793c1f5f3d

View File

@@ -173,26 +173,72 @@ function buildScene(dim, sceneIndex, x, surfY, z) {
// ─── Egg Placement ─────────────────────────────────────────────── // ─── Egg Placement ───────────────────────────────────────────────
function placeAllEggs() { /**
if (world.getDynamicProperty("eggs_placed") === "true") return; * Place all eggs around a centre position.
* On first call: centre comes from the joining player's location.
const spawn = world.getDefaultSpawnLocation(); * Centre is stored so all players (and resets) use the same area.
*
* If eggs_placed is "true" but no egg blocks actually exist in the world
* (e.g. blocks were never written because chunks weren't loaded), the flag
* is cleared automatically and eggs are re-placed.
*/
function placeAllEggs(playerPos) {
const overworld = world.getDimension("overworld"); const overworld = world.getDimension("overworld");
if (world.getDynamicProperty("eggs_placed") === "true") {
// Validate at least one stored egg block actually exists
const stored = getEggPositions();
if (stored && stored.length > 0) {
let found = 0;
for (let i = 0; i < Math.min(5, stored.length); i++) {
try {
const block = overworld.getBlock({ x: stored[i].x, y: stored[i].y, z: stored[i].z });
if (block && block.typeId.includes("glazed_terracotta")) found++;
} catch (e) {}
}
if (found > 0) return; // eggs are genuinely there
}
// Eggs flagged as placed but none found — clear and re-place
world.setDynamicProperty("eggs_placed", "false");
world.setDynamicProperty("eggs_data", "");
world.setDynamicProperty("egg_center", "");
eggPositions = null;
}
// Determine centre: use stored centre, or the joining player's position
let cx, cy, cz;
try {
const stored = JSON.parse(world.getDynamicProperty("egg_center") || "null");
if (stored) { cx = stored.x; cy = stored.y; cz = stored.z; }
} catch (e) {}
if (cx === undefined) {
if (playerPos) {
cx = Math.floor(playerPos.x);
cy = Math.floor(playerPos.y);
cz = Math.floor(playerPos.z);
} else {
const spawn = world.getDefaultSpawnLocation();
cx = Math.floor(spawn.x); cy = Math.floor(spawn.y); cz = Math.floor(spawn.z);
}
try { world.setDynamicProperty("egg_center", JSON.stringify({ x: cx, y: cy, z: cz })); } catch (e) {}
}
const positions = []; const positions = [];
for (let i = 0; i < EGG_OFFSETS.length; i++) { for (let i = 0; i < EGG_OFFSETS.length; i++) {
const [dx, dz] = EGG_OFFSETS[i]; const [dx, dz] = EGG_OFFSETS[i];
const x = Math.floor(spawn.x) + dx; const x = cx + dx;
const z = Math.floor(spawn.z) + dz; const z = cz + dz;
const surfY = findSurfaceY(overworld, x, Math.floor(spawn.y), z); const surfY = findSurfaceY(overworld, x, cy, z);
const eggY = buildScene(overworld, i, x, surfY, z); const eggY = buildScene(overworld, i, x, surfY, z);
positions.push({ x, y: eggY, z, id: i + 1 }); positions.push({ x, y: eggY, z, id: i + 1 });
} }
try { try {
world.setDynamicProperty("eggs_data", JSON.stringify(positions)); world.setDynamicProperty("eggs_data", JSON.stringify(positions));
world.setDynamicProperty("eggs_placed", "true"); world.setDynamicProperty("eggs_placed", "true");
world.sendMessage(`§6[Easter Eggs] §f${positions.length} eggs are now hidden around spawn — happy hunting!`); world.sendMessage(`§6[Easter Eggs] §f${positions.length} eggs are now hidden around here — happy hunting!`);
} catch (e) { } catch (e) {
world.sendMessage(`§c[Easter Eggs] §fFailed to save egg data: ${e.message}`); world.sendMessage(`§c[Easter Eggs] §fFailed to save egg data: ${e.message}`);
} }
@@ -292,10 +338,13 @@ function handleEggCommand(player, msg) {
try { try {
world.setDynamicProperty("eggs_placed", "false"); world.setDynamicProperty("eggs_placed", "false");
world.setDynamicProperty("eggs_data", ""); world.setDynamicProperty("eggs_data", "");
world.setDynamicProperty("egg_center", "");
} catch (e) {} } catch (e) {}
eggPositions = null; eggPositions = null;
player.sendMessage("§6[Eggs] §fEgg data cleared — re-placing all eggs..."); eggsPlacedThisSession = false;
system.runTimeout(() => placeAllEggs(), 20); player.sendMessage("§6[Eggs] §fEgg data cleared — eggs will re-place around your position...");
const pos = player.location;
system.runTimeout(() => placeAllEggs(pos), 20);
return true; return true;
} }
@@ -323,10 +372,19 @@ system.afterEvents.scriptEventReceive.subscribe((event) => {
}); });
// ─── Startup ───────────────────────────────────────────────────── // ─── Startup ─────────────────────────────────────────────────────
// Egg placement fires on first player spawn, not server startup.
// This guarantees spawn chunks are loaded before setblock commands run.
system.runTimeout(() => { let eggsPlacedThisSession = false;
placeAllEggs();
}, 120); // 6 second delay to allow world chunks to load near spawn world.afterEvents.playerSpawn.subscribe((event) => {
if (eggsPlacedThisSession) return;
eggsPlacedThisSession = true;
// Capture player position now; chunks around them are guaranteed loaded
const playerPos = event.player.location;
// Short delay so the player is fully settled before blocks are placed
system.runTimeout(() => placeAllEggs(playerPos), 40);
});
system.run(() => { system.run(() => {
world.sendMessage("§6[Easter Eggs] §fHunt active! Type §e!eggs §fto check your progress."); world.sendMessage("§6[Easter Eggs] §fHunt active! Type §e!eggs §fto check your progress.");