Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.19 #663

Merged
merged 15 commits into from
Jan 4, 2025
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', {
Copy link
Member

Choose a reason for hiding this comment

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

looks like this is completely removed even for before 1.19 ; is this expected?

Copy link
Member Author

Choose a reason for hiding this comment

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

This sends the latency for all the clients but the previous player_info packet already sends the latencies in its payload with action mode 0

Back to 1.8 https://github.com/PrismarineJS/minecraft-data/blob/master/data/pc/1.8/proto.yml#L565

I think it may have been added because it's what vanilla did at some point but it wasn't doing that for 1.17. I could re-add it behind a flag for <1.17

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

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
Loading