Skip to content

Commit c11dff3

Browse files
authored
1.19 (#663)
* 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-
1 parent acbee30 commit c11dff3

File tree

13 files changed

+174
-95
lines changed

13 files changed

+174
-95
lines changed

config/default-settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@
2525
},
2626
"everybody-op": false,
2727
"max-entities":100,
28-
"version": "1.18.2"
28+
"version": "1.19.4"
2929
}

docs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ flying-squid
1111
Create Minecraft servers with a powerful, stable, and high level JavaScript API.
1212

1313
## Features
14-
* 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
14+
* 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
1515
* Players can see the world
1616
* Players see each other in-game and in tab
1717
* Digging

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"prepublishOnly": "cp docs/README.md README.md",
1616
"lint": "standard",
1717
"fix": "standard --fix",
18-
"mocha_test": "mocha --reporter spec --timeout 30000 --exit",
18+
"mocha_test": "mocha --reporter spec --timeout 30000 --retries 2 --exit",
1919
"test": "npm run mocha_test",
2020
"pretest": "npm run lint"
2121
},

src/index.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,15 @@ class MCServer extends EventEmitter {
4646

4747
const versionData = registry.version
4848
if (versionData['>'](latestSupportedVersion)) {
49-
throw new Error(`Server version '${registry?.version}' is not supported. Latest supported version is '${latestSupportedVersion}'.`)
49+
throw new Error(`Server version '${options.version}' is not supported. Latest supported version is '${latestSupportedVersion}'.`)
5050
} else if (versionData['<'](oldestSupportedVersion)) {
51-
throw new Error(`Server version '${registry?.version}' is not supported. Oldest supported version is '${oldestSupportedVersion}'.`)
51+
throw new Error(`Server version '${options.version}' is not supported. Oldest supported version is '${oldestSupportedVersion}'.`)
5252
}
5353

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

5759
const promises = []
5860
for (const plugin of plugins.builtinPlugins) {

src/lib/plugins/chat.js

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,31 @@ module.exports.server = function (serv) {
120120
}
121121

122122
module.exports.player = function (player, serv) {
123+
// 1.19+ -- from nmp server example - not implementing chat singing yet, so all messages are sent as system_chat
124+
function handleChatMessage (data) {
125+
const fmtMessage = `<${player.username}> ${data.message}`
126+
serv.broadcast(fmtMessage, { whitelist: serv.players, blacklist: [] })
127+
}
128+
129+
player._client.on('chat_message', (data) => {
130+
player.behavior('chat', {
131+
message: data.message,
132+
prefix: '<' + player.username + '> ',
133+
text: data.message,
134+
whitelist: serv.players,
135+
blacklist: [],
136+
data
137+
}, ({ data }) => {
138+
handleChatMessage(data)
139+
})
140+
})
141+
player._client.on('chat_command', (data) => {
142+
const command = data.command
143+
player.behavior('command', { command }, ({ command }) => {
144+
player.handleCommand(command)
145+
})
146+
})
147+
123148
player._client.on('chat', ({ message } = {}) => {
124149
if (message[0] === '/') {
125150
player.behavior('command', { command: message.slice(1) }, ({ command }) => player.handleCommand(command))
@@ -144,8 +169,16 @@ module.exports.player = function (player, serv) {
144169
})
145170

146171
player.chat = message => {
147-
if (typeof message === 'string') message = serv.parseClassic(message)
148-
player._client.write('chat', { message: JSON.stringify(message), position: 0, sender: '0' })
172+
if (serv.supportFeature('signedChat')) {
173+
return player.system(message)
174+
} else {
175+
const chatComponent = typeof message === 'string' ? serv.parseClassic(message) : message
176+
player._client.write('chat', {
177+
message: JSON.stringify(chatComponent),
178+
position: 0,
179+
sender: '0'
180+
})
181+
}
149182
}
150183

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

157190
player.system = message => {
158-
if (typeof message === 'string') message = serv.parseClassic(message)
159-
player._client.write('chat', { message: JSON.stringify(message), position: 2, sender: '0' })
191+
const chatComponent = typeof message === 'string' ? serv.parseClassic(message) : message
192+
if (serv.supportFeature('signedChat')) {
193+
player._client.write('system_chat', {
194+
content: JSON.stringify(chatComponent),
195+
type: 1, // chat
196+
isActionBar: false
197+
})
198+
} else {
199+
player._client.write('chat', {
200+
message: JSON.stringify(chatComponent),
201+
position: 2,
202+
sender: '0'
203+
})
204+
}
160205
}
161206
}

src/lib/plugins/digging.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module.exports.player = function (player, serv, { version }) {
66
player.sendBlock(position, block.type)
77
}
88

9-
player._client.on('block_dig', async ({ location, status, face }) => {
9+
player._client.on('block_dig', async ({ location, status, face, sequence }) => {
1010
if (status === 3 || status === 4) {
1111
const heldItem = player.inventory.slots[36 + player.heldItemSlot]
1212
if (!heldItem || heldItem.type === -1) return
@@ -56,12 +56,12 @@ module.exports.player = function (player, serv, { version }) {
5656
if (player.gameMode === 1) {
5757
creativeDigging(pos)
5858
} else {
59-
startDigging(pos)
59+
startDigging(pos, sequence)
6060
}
6161
} else if (status === 1 || player.gameMode >= 2) {
62-
cancelDigging(pos)
62+
cancelDigging(pos, sequence)
6363
} else if (status === 2) {
64-
completeDigging(pos)
64+
completeDigging(pos, sequence)
6565
}
6666
}
6767
})
@@ -77,7 +77,7 @@ module.exports.player = function (player, serv, { version }) {
7777
let expectedDiggingTime
7878
let lastDestroyState
7979
let currentAnimationId
80-
function startDigging (location) {
80+
function startDigging (location, sequenceId) {
8181
serv.entityMaxId++
8282
currentAnimationId = serv.entityMaxId
8383
expectedDiggingTime = diggingTime(location)
@@ -108,6 +108,7 @@ module.exports.player = function (player, serv, { version }) {
108108
}
109109
if (serv.supportFeature('acknowledgePlayerDigging')) {
110110
player._client.write('acknowledge_player_digging', {
111+
sequenceId, // 1.19
111112
location,
112113
block: currentlyDugBlock.stateId,
113114
status: 0,
@@ -116,7 +117,7 @@ module.exports.player = function (player, serv, { version }) {
116117
}
117118
}
118119

119-
function cancelDigging (location) {
120+
function cancelDigging (location, sequenceId) {
120121
clearInterval(animationInterval)
121122
player._writeOthersNearby('block_break_animation', {
122123
entityId: currentAnimationId,
@@ -125,6 +126,7 @@ module.exports.player = function (player, serv, { version }) {
125126
})
126127
if (serv.supportFeature('acknowledgePlayerDigging')) {
127128
player._client.write('acknowledge_player_digging', {
129+
sequenceId, // 1.19
128130
location,
129131
block: currentlyDugBlock.stateId,
130132
status: 1,
@@ -133,7 +135,7 @@ module.exports.player = function (player, serv, { version }) {
133135
}
134136
}
135137

136-
async function completeDigging (location) {
138+
async function completeDigging (location, sequenceId) {
137139
clearInterval(animationInterval)
138140
const diggingTime = new Date() - startDiggingTime
139141
let stop = false
@@ -187,6 +189,7 @@ module.exports.player = function (player, serv, { version }) {
187189
}
188190
if (serv.supportFeature('acknowledgePlayerDigging')) {
189191
player._client.write('acknowledge_player_digging', {
192+
sequenceId, // 1.19
190193
location,
191194
block: 0,
192195
status: 2,
@@ -201,6 +204,7 @@ module.exports.player = function (player, serv, { version }) {
201204
})
202205
if (serv.supportFeature('acknowledgePlayerDigging')) {
203206
player._client.write('acknowledge_player_digging', {
207+
sequenceId, // 1.19
204208
location,
205209
block: currentlyDugBlock.stateId,
206210
status: 2,

src/lib/plugins/login.js

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -136,24 +136,46 @@ module.exports.player = async function (player, serv, settings) {
136136
})
137137
}
138138

139-
player.setGameMode = (gameMode) => {
140-
if (gameMode !== player.gameMode) player.prevGameMode = player.gameMode
141-
player.gameMode = gameMode
142-
player._client.write('game_state_change', {
143-
reason: 3,
144-
gameMode: player.gameMode
139+
// TODO: The structure of player_info changes alot between versions and is messy
140+
// https://github.com/PrismarineJS/minecraft-data/pull/948 will fix some of it but
141+
// merging that will also require updating mineflayer. In the meantime we can skip this
142+
// packet in 1.19+ as it also requires some chat signing key logic to be implemented
143+
144+
serv._sendPlayerEventLeave = function (player) {
145+
if (serv.registry.version['>=']('1.19')) return
146+
player._writeOthers('player_info', {
147+
action: 4,
148+
data: [{
149+
UUID: player.uuid,
150+
uuid: player.uuid // 1.19.3+
151+
}]
145152
})
153+
}
154+
155+
serv._sendPlayerEventUpdateGameMode = function (player) {
156+
if (serv.registry.version['>=']('1.19')) return
146157
serv._writeAll('player_info', {
147158
action: 1,
148159
data: [{
149160
UUID: player.uuid,
150161
gamemode: player.gameMode
151162
}]
152163
})
164+
}
165+
166+
player.setGameMode = (gameMode) => {
167+
if (gameMode !== player.gameMode) player.prevGameMode = player.gameMode
168+
player.gameMode = gameMode
169+
player._client.write('game_state_change', {
170+
reason: 3,
171+
gameMode: player.gameMode
172+
})
173+
serv._sendPlayerEventUpdateGameMode(player)
153174
player.sendAbilities()
154175
}
155176

156-
function fillTabList () {
177+
serv._sendPlayerEventNewJoin = function (player) {
178+
if (serv.registry.version['>=']('1.19')) return
157179
player._writeOthers('player_info', {
158180
action: 0,
159181
data: [{
@@ -164,8 +186,11 @@ module.exports.player = async function (player, serv, settings) {
164186
ping: player._client.latency
165187
}]
166188
})
189+
}
167190

168-
player._client.write('player_info', {
191+
serv._sendPlayerList = function (toPlayer) {
192+
if (serv.registry.version['>=']('1.19')) return
193+
toPlayer._writeOthers('player_info', {
169194
action: 0,
170195
data: serv.players.map((otherPlayer) => ({
171196
UUID: otherPlayer.uuid,
@@ -175,13 +200,19 @@ module.exports.player = async function (player, serv, settings) {
175200
ping: otherPlayer._client.latency
176201
}))
177202
})
178-
setInterval(() => player._client.write('player_info', {
179-
action: 2,
180-
data: serv.players.map(otherPlayer => ({
181-
UUID: otherPlayer.uuid,
182-
ping: otherPlayer._client.latency
183-
}))
184-
}), 5000)
203+
}
204+
205+
function fillTabList () {
206+
serv._sendPlayerList(player)
207+
if (serv.registry.version['<=']('1.18')) {
208+
setInterval(() => player._client.write('player_info', {
209+
action: 2,
210+
data: serv.players.map(otherPlayer => ({
211+
UUID: otherPlayer.uuid,
212+
ping: otherPlayer._client.latency
213+
}))
214+
}), 5000)
215+
}
185216
}
186217

187218
function announceJoin () {

src/lib/plugins/logout.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,7 @@ module.exports.player = function (player, serv, { worldFolder }) {
2020
if (player && player.username) {
2121
player._unloadAllChunks()
2222
serv.broadcast(serv.color.yellow + player.username + ' left the game.')
23-
player._writeOthers('player_info', {
24-
action: 4,
25-
data: [{
26-
UUID: player.uuid
27-
}]
28-
})
23+
serv._sendPlayerEventLeave(player)
2924
player.nearbyPlayers().forEach(otherPlayer => otherPlayer.despawnEntities([player]))
3025
delete serv.entities[player.id]
3126
player.emit('disconnected')

src/lib/plugins/sound.js

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,34 @@ module.exports.server = function (serv, { version }) {
1616
.forEach(player => {
1717
const iniPos = position ? position.scaled(1 / 32) : player.position.scaled(1 / 32)
1818
const pos = iniPos.scaled(8).floored()
19+
if (serv.supportFeature('removedNamedSoundEffectPacket')) { // 1.19.3 removes named_sound_effect
20+
player._client.write('sound_effect', {
21+
soundId: 0,
22+
soundEvent: {
23+
resource: sound,
24+
range: undefined
25+
},
26+
soundCategory,
27+
x: pos.x,
28+
y: pos.y,
29+
z: pos.z,
30+
volume,
31+
pitch: Math.round(pitch * 63),
32+
seed: 0
33+
})
34+
} else {
1935
// only packet still in fixed position in all versions
20-
player._client.write('named_sound_effect', {
21-
soundName: sound,
22-
soundCategory,
23-
x: pos.x,
24-
y: pos.y,
25-
z: pos.z,
26-
volume,
27-
pitch: Math.round(pitch * 63)
28-
})
36+
player._client.write('named_sound_effect', {
37+
soundName: sound,
38+
soundCategory,
39+
x: pos.x,
40+
y: pos.y,
41+
z: pos.z,
42+
volume,
43+
pitch: Math.round(pitch * 63),
44+
seed: 0
45+
})
46+
}
2947
})
3048
}
3149

@@ -49,7 +67,8 @@ module.exports.server = function (serv, { version }) {
4967
y: pos.y,
5068
z: pos.z,
5169
volume,
52-
pitch: Math.round(pitch * 63)
70+
pitch: Math.round(pitch * 63),
71+
seed: 0
5372
})
5473
})
5574
}

0 commit comments

Comments
 (0)