Skip to content

Commit

Permalink
1.19 (#663)
Browse files Browse the repository at this point in the history
* 1.18 and 1.18.2 support

* test fix

* wait for spawn event in tests over login

* revert world sendNearbyChunks change

* 1.19 support

* fix consolidatedEntitySpawnPacket to use spawn_entity

* fix player_info handling for 1.19

* cleanup

* mocha --retry 2

* typo

* bump mocha

* Update ci.yml

* Update login.js re-add player_info with latency for 1.17-
  • Loading branch information
extremeheat authored Jan 4, 2025
1 parent acbee30 commit c11dff3
Show file tree
Hide file tree
Showing 13 changed files with 174 additions and 95 deletions.
2 changes: 1 addition & 1 deletion config/default-settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@
},
"everybody-op": false,
"max-entities":100,
"version": "1.18.2"
"version": "1.19.4"
}
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ flying-squid
Create Minecraft servers with a powerful, stable, and high level JavaScript API.

## Features
* Support for Minecraft 1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17 and 1.18
* Support for Minecraft 1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19
* Players can see the world
* Players see each other in-game and in tab
* Digging
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"prepublishOnly": "cp docs/README.md README.md",
"lint": "standard",
"fix": "standard --fix",
"mocha_test": "mocha --reporter spec --timeout 30000 --exit",
"mocha_test": "mocha --reporter spec --timeout 30000 --retries 2 --exit",
"test": "npm run mocha_test",
"pretest": "npm run lint"
},
Expand Down
8 changes: 5 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,15 @@ class MCServer extends EventEmitter {

const versionData = registry.version
if (versionData['>'](latestSupportedVersion)) {
throw new Error(`Server version '${registry?.version}' is not supported. Latest supported version is '${latestSupportedVersion}'.`)
throw new Error(`Server version '${options.version}' is not supported. Latest supported version is '${latestSupportedVersion}'.`)
} else if (versionData['<'](oldestSupportedVersion)) {
throw new Error(`Server version '${registry?.version}' is not supported. Oldest supported version is '${oldestSupportedVersion}'.`)
throw new Error(`Server version '${options.version}' is not supported. Oldest supported version is '${oldestSupportedVersion}'.`)
}

// internal features until merged into minecraft-data
const customFeatures = {}
this.registry = registry
this.supportFeature = registry.supportFeature
this.supportFeature = feature => customFeatures[feature] ?? registry.supportFeature(feature)

const promises = []
for (const plugin of plugins.builtinPlugins) {
Expand Down
53 changes: 49 additions & 4 deletions src/lib/plugins/chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,31 @@ module.exports.server = function (serv) {
}

module.exports.player = function (player, serv) {
// 1.19+ -- from nmp server example - not implementing chat singing yet, so all messages are sent as system_chat
function handleChatMessage (data) {
const fmtMessage = `<${player.username}> ${data.message}`
serv.broadcast(fmtMessage, { whitelist: serv.players, blacklist: [] })
}

player._client.on('chat_message', (data) => {
player.behavior('chat', {
message: data.message,
prefix: '<' + player.username + '> ',
text: data.message,
whitelist: serv.players,
blacklist: [],
data
}, ({ data }) => {
handleChatMessage(data)
})
})
player._client.on('chat_command', (data) => {
const command = data.command
player.behavior('command', { command }, ({ command }) => {
player.handleCommand(command)
})
})

player._client.on('chat', ({ message } = {}) => {
if (message[0] === '/') {
player.behavior('command', { command: message.slice(1) }, ({ command }) => player.handleCommand(command))
Expand All @@ -144,8 +169,16 @@ module.exports.player = function (player, serv) {
})

player.chat = message => {
if (typeof message === 'string') message = serv.parseClassic(message)
player._client.write('chat', { message: JSON.stringify(message), position: 0, sender: '0' })
if (serv.supportFeature('signedChat')) {
return player.system(message)
} else {
const chatComponent = typeof message === 'string' ? serv.parseClassic(message) : message
player._client.write('chat', {
message: JSON.stringify(chatComponent),
position: 0,
sender: '0'
})
}
}

player.emptyChat = (count = 1) => {
Expand All @@ -155,7 +188,19 @@ module.exports.player = function (player, serv) {
}

player.system = message => {
if (typeof message === 'string') message = serv.parseClassic(message)
player._client.write('chat', { message: JSON.stringify(message), position: 2, sender: '0' })
const chatComponent = typeof message === 'string' ? serv.parseClassic(message) : message
if (serv.supportFeature('signedChat')) {
player._client.write('system_chat', {
content: JSON.stringify(chatComponent),
type: 1, // chat
isActionBar: false
})
} else {
player._client.write('chat', {
message: JSON.stringify(chatComponent),
position: 2,
sender: '0'
})
}
}
}
18 changes: 11 additions & 7 deletions src/lib/plugins/digging.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module.exports.player = function (player, serv, { version }) {
player.sendBlock(position, block.type)
}

player._client.on('block_dig', async ({ location, status, face }) => {
player._client.on('block_dig', async ({ location, status, face, sequence }) => {
if (status === 3 || status === 4) {
const heldItem = player.inventory.slots[36 + player.heldItemSlot]
if (!heldItem || heldItem.type === -1) return
Expand Down Expand Up @@ -56,12 +56,12 @@ module.exports.player = function (player, serv, { version }) {
if (player.gameMode === 1) {
creativeDigging(pos)
} else {
startDigging(pos)
startDigging(pos, sequence)
}
} else if (status === 1 || player.gameMode >= 2) {
cancelDigging(pos)
cancelDigging(pos, sequence)
} else if (status === 2) {
completeDigging(pos)
completeDigging(pos, sequence)
}
}
})
Expand All @@ -77,7 +77,7 @@ module.exports.player = function (player, serv, { version }) {
let expectedDiggingTime
let lastDestroyState
let currentAnimationId
function startDigging (location) {
function startDigging (location, sequenceId) {
serv.entityMaxId++
currentAnimationId = serv.entityMaxId
expectedDiggingTime = diggingTime(location)
Expand Down Expand Up @@ -108,6 +108,7 @@ module.exports.player = function (player, serv, { version }) {
}
if (serv.supportFeature('acknowledgePlayerDigging')) {
player._client.write('acknowledge_player_digging', {
sequenceId, // 1.19
location,
block: currentlyDugBlock.stateId,
status: 0,
Expand All @@ -116,7 +117,7 @@ module.exports.player = function (player, serv, { version }) {
}
}

function cancelDigging (location) {
function cancelDigging (location, sequenceId) {
clearInterval(animationInterval)
player._writeOthersNearby('block_break_animation', {
entityId: currentAnimationId,
Expand All @@ -125,6 +126,7 @@ module.exports.player = function (player, serv, { version }) {
})
if (serv.supportFeature('acknowledgePlayerDigging')) {
player._client.write('acknowledge_player_digging', {
sequenceId, // 1.19
location,
block: currentlyDugBlock.stateId,
status: 1,
Expand All @@ -133,7 +135,7 @@ module.exports.player = function (player, serv, { version }) {
}
}

async function completeDigging (location) {
async function completeDigging (location, sequenceId) {
clearInterval(animationInterval)
const diggingTime = new Date() - startDiggingTime
let stop = false
Expand Down Expand Up @@ -187,6 +189,7 @@ module.exports.player = function (player, serv, { version }) {
}
if (serv.supportFeature('acknowledgePlayerDigging')) {
player._client.write('acknowledge_player_digging', {
sequenceId, // 1.19
location,
block: 0,
status: 2,
Expand All @@ -201,6 +204,7 @@ module.exports.player = function (player, serv, { version }) {
})
if (serv.supportFeature('acknowledgePlayerDigging')) {
player._client.write('acknowledge_player_digging', {
sequenceId, // 1.19
location,
block: currentlyDugBlock.stateId,
status: 2,
Expand Down
61 changes: 46 additions & 15 deletions src/lib/plugins/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,24 +136,46 @@ module.exports.player = async function (player, serv, settings) {
})
}

player.setGameMode = (gameMode) => {
if (gameMode !== player.gameMode) player.prevGameMode = player.gameMode
player.gameMode = gameMode
player._client.write('game_state_change', {
reason: 3,
gameMode: player.gameMode
// TODO: The structure of player_info changes alot between versions and is messy
// https://github.com/PrismarineJS/minecraft-data/pull/948 will fix some of it but
// merging that will also require updating mineflayer. In the meantime we can skip this
// packet in 1.19+ as it also requires some chat signing key logic to be implemented

serv._sendPlayerEventLeave = function (player) {
if (serv.registry.version['>=']('1.19')) return
player._writeOthers('player_info', {
action: 4,
data: [{
UUID: player.uuid,
uuid: player.uuid // 1.19.3+
}]
})
}

serv._sendPlayerEventUpdateGameMode = function (player) {
if (serv.registry.version['>=']('1.19')) return
serv._writeAll('player_info', {
action: 1,
data: [{
UUID: player.uuid,
gamemode: player.gameMode
}]
})
}

player.setGameMode = (gameMode) => {
if (gameMode !== player.gameMode) player.prevGameMode = player.gameMode
player.gameMode = gameMode
player._client.write('game_state_change', {
reason: 3,
gameMode: player.gameMode
})
serv._sendPlayerEventUpdateGameMode(player)
player.sendAbilities()
}

function fillTabList () {
serv._sendPlayerEventNewJoin = function (player) {
if (serv.registry.version['>=']('1.19')) return
player._writeOthers('player_info', {
action: 0,
data: [{
Expand All @@ -164,8 +186,11 @@ module.exports.player = async function (player, serv, settings) {
ping: player._client.latency
}]
})
}

player._client.write('player_info', {
serv._sendPlayerList = function (toPlayer) {
if (serv.registry.version['>=']('1.19')) return
toPlayer._writeOthers('player_info', {
action: 0,
data: serv.players.map((otherPlayer) => ({
UUID: otherPlayer.uuid,
Expand All @@ -175,13 +200,19 @@ module.exports.player = async function (player, serv, settings) {
ping: otherPlayer._client.latency
}))
})
setInterval(() => player._client.write('player_info', {
action: 2,
data: serv.players.map(otherPlayer => ({
UUID: otherPlayer.uuid,
ping: otherPlayer._client.latency
}))
}), 5000)
}

function fillTabList () {
serv._sendPlayerList(player)
if (serv.registry.version['<=']('1.18')) {
setInterval(() => player._client.write('player_info', {
action: 2,
data: serv.players.map(otherPlayer => ({
UUID: otherPlayer.uuid,
ping: otherPlayer._client.latency
}))
}), 5000)
}
}

function announceJoin () {
Expand Down
7 changes: 1 addition & 6 deletions src/lib/plugins/logout.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,7 @@ module.exports.player = function (player, serv, { worldFolder }) {
if (player && player.username) {
player._unloadAllChunks()
serv.broadcast(serv.color.yellow + player.username + ' left the game.')
player._writeOthers('player_info', {
action: 4,
data: [{
UUID: player.uuid
}]
})
serv._sendPlayerEventLeave(player)
player.nearbyPlayers().forEach(otherPlayer => otherPlayer.despawnEntities([player]))
delete serv.entities[player.id]
player.emit('disconnected')
Expand Down
39 changes: 29 additions & 10 deletions src/lib/plugins/sound.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,34 @@ module.exports.server = function (serv, { version }) {
.forEach(player => {
const iniPos = position ? position.scaled(1 / 32) : player.position.scaled(1 / 32)
const pos = iniPos.scaled(8).floored()
if (serv.supportFeature('removedNamedSoundEffectPacket')) { // 1.19.3 removes named_sound_effect
player._client.write('sound_effect', {
soundId: 0,
soundEvent: {
resource: sound,
range: undefined
},
soundCategory,
x: pos.x,
y: pos.y,
z: pos.z,
volume,
pitch: Math.round(pitch * 63),
seed: 0
})
} else {
// only packet still in fixed position in all versions
player._client.write('named_sound_effect', {
soundName: sound,
soundCategory,
x: pos.x,
y: pos.y,
z: pos.z,
volume,
pitch: Math.round(pitch * 63)
})
player._client.write('named_sound_effect', {
soundName: sound,
soundCategory,
x: pos.x,
y: pos.y,
z: pos.z,
volume,
pitch: Math.round(pitch * 63),
seed: 0
})
}
})
}

Expand All @@ -49,7 +67,8 @@ module.exports.server = function (serv, { version }) {
y: pos.y,
z: pos.z,
volume,
pitch: Math.round(pitch * 63)
pitch: Math.round(pitch * 63),
seed: 0
})
})
}
Expand Down
Loading

0 comments on commit c11dff3

Please sign in to comment.