Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"vec3": "^0.1.7"
},
"devDependencies": {
"@types/node": "^24.0.6",
"@types/node": "^25.2.1",
"doctoc": "^2.0.1",
"minecraft-wrap": "^1.3.0",
"mineflayer": "file:",
Expand Down
11 changes: 9 additions & 2 deletions test/externalTests/nether.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const assert = require('assert')
const Vec3 = require('vec3')
const { once } = require('../../lib/promise_utils')
const { once, sleep } = require('../../lib/promise_utils')

module.exports = () => async (bot) => {
// Test spawn event on death
Expand Down Expand Up @@ -46,7 +46,14 @@ module.exports = () => async (bot) => {
await once(bot, 'forcedMove')
await bot.waitForChunksToLoad()

const lowerBlock = bot.blockAt(bot.entity.position.offset(0, -1, 0))
// Poll until the block below is loaded and non-air before placing.
// On slow CI, chunks may report as loaded before block data is ready.
let lowerBlock = bot.blockAt(bot.entity.position.offset(0, -1, 0))
while (!lowerBlock || lowerBlock.name === 'air') {
await sleep(100)
lowerBlock = bot.blockAt(bot.entity.position.offset(0, -1, 0))
}

await bot.lookAt(lowerBlock.position, true)
await bot.test.setInventorySlot(36, new Item(signItem.id, 1, 0))
await bot.placeBlock(lowerBlock, new Vec3(0, 1, 0))
Expand Down
26 changes: 18 additions & 8 deletions test/externalTests/plugins/testCommon.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ const { spawn } = require('child_process')
const { once } = require('../../../lib/promise_utils')
const process = require('process')
const assert = require('assert')
const { sleep, onceWithCleanup, withTimeout } = require('../../../lib/promise_utils')
const { sleep, onceWithCleanup } = require('../../../lib/promise_utils')

const timeout = 5000
const timeout = 20000
module.exports = inject

function inject (bot) {
Expand Down Expand Up @@ -188,14 +188,21 @@ function inject (bot) {

const detectChildJoin = async () => {
const [message] = await onceWithCleanup(bot, 'message', {
timeout,
checkCondition: message => message.json.translate === 'multiplayer.player.joined'
})
childBotName = message.json.with[0].insertion
bot.chat(`/tp ${childBotName} 50 ${bot.test.groundY} 0`)
setTimeout(() => {
bot.chat('loaded')
}, 5000)
// Wait for the child entity to arrive at the teleport target,
// confirming the server has processed the TP
const targetPos = new Vec3(50, bot.test.groundY, 0)
while (!bot.players[childBotName]?.entity ||
bot.players[childBotName].entity.position.distanceTo(targetPos) > 5) {
await sleep(100)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much better to use events than this kind of checking every 100ms

}
// Let the child's physics engine initialize at the new position
// (ground detection, chunk processing) before starting the test
await bot.waitForTicks(60)
bot.chat('loaded')
}

const runExampleOnReady = async () => {
Expand All @@ -216,7 +223,7 @@ function inject (bot) {

try {
process.kill(child.pid, 'SIGTERM')
const [code] = await onceWithCleanup(child, 'close', { timeout: 1000 })
const [code] = await onceWithCleanup(child, 'close', { timeout: 5000 })
console.log('close requested', code)
} catch (e) {
console.log(e)
Expand All @@ -228,8 +235,11 @@ function inject (bot) {
}
}

// Let mocha's test-level timeout (90s) be the backstop instead of
// an inner withTimeout, which was causing premature failures on
// slow CI runners.
try {
await withTimeout(Promise.all([detectChildJoin(), runExampleOnReady()]), 30000)
await Promise.all([detectChildJoin(), runExampleOnReady()])
} catch (err) {
console.log(err)
return closeExample(err)
Expand Down
42 changes: 30 additions & 12 deletions test/externalTests/time.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ module.exports = () => async (bot) => {
}
}

// Helper to set gamerule using the correct name for the version
const setDaylightCycle = (value) => {
if (bot.supportFeature('gameRuleUsesResourceLocation')) {
bot.test.sayEverywhere(`/gamerule minecraft:advance_time ${value}`)
} else {
bot.test.sayEverywhere(`/gamerule doDaylightCycle ${value}`)
}
}

// Disable daylight cycle before time transition tests to prevent
// time from drifting between /time set and the assertion
const originalDaylightCycle = bot.time.doDaylightCycle
setDaylightCycle(false)
await waitForTime()

// Test time transitions
const timeTests = [
{ time: 18000, name: 'midnight', isDay: false },
Expand All @@ -58,6 +73,10 @@ module.exports = () => async (bot) => {
assert.strictEqual(bot.time.isDay, test.isDay, `${test.name} should be ${test.isDay ? 'day' : 'night'}`)
}

// Re-enable daylight cycle for progression test
setDaylightCycle(true)
await waitForTime()

// Test day and moon phase progression
const currentDay = bot.time.day
const currentPhase = bot.time.moonPhase
Expand All @@ -66,24 +85,19 @@ module.exports = () => async (bot) => {
assert(bot.time.day >= currentDay + 1, `Expected day to be at least ${currentDay + 1}, got ${bot.time.day}`)
assert.notStrictEqual(bot.time.moonPhase, currentPhase, 'Moon phase should change after a full day')

// Test daylight cycle
const originalDaylightCycle = bot.time.doDaylightCycle
if (bot.supportFeature('gameRuleUsesResourceLocation')) {
bot.test.sayEverywhere('/gamerule minecraft:advance_time false')
} else {
bot.test.sayEverywhere('/gamerule doDaylightCycle false')
}
// Test daylight cycle toggle
setDaylightCycle(false)
await waitForTime()
assert.strictEqual(bot.time.doDaylightCycle, false)

if (bot.supportFeature('gameRuleUsesResourceLocation')) {
bot.test.sayEverywhere(`/gamerule minecraft:advance_time ${originalDaylightCycle}`)
} else {
bot.test.sayEverywhere(`/gamerule doDaylightCycle ${originalDaylightCycle}`)
}
setDaylightCycle(originalDaylightCycle)
await waitForTime()
assert.strictEqual(bot.time.doDaylightCycle, originalDaylightCycle)

// Disable daylight cycle again for day/night range tests
setDaylightCycle(false)
await waitForTime()

// Test day/night transitions
const dayNightTests = [
{ command: 'day', range: [0, 12000], isDay: true },
Expand All @@ -96,4 +110,8 @@ module.exports = () => async (bot) => {
assert(isTimeInRange(bot.time.timeOfDay, test.range[0], test.range[1]), `Time should be in ${test.command} range`)
assert.strictEqual(bot.time.isDay, test.isDay, `${test.command} should be ${test.isDay ? 'day' : 'night'}`)
}

// Restore original daylight cycle setting
setDaylightCycle(originalDaylightCycle)
await waitForTime()
}
Loading