From d27da693aae33a22470f18c467bfadaf3d135008 Mon Sep 17 00:00:00 2001 From: U9G Date: Thu, 15 Dec 2022 12:39:19 -0500 Subject: [PATCH] heavily WIP --- examples/client_chat/client_chat.js | 3 +- src/client/chat.js | 110 +++++++++++++++--- src/client/chatPreviewModes.js | 5 + .../chat_activity_bring_to_mineflayer.js | 24 ++++ src/client/setProtocol.js | 5 +- src/createClient.js | 2 + 6 files changed, 131 insertions(+), 18 deletions(-) create mode 100644 src/client/chatPreviewModes.js create mode 100644 src/client/chat_activity_bring_to_mineflayer.js diff --git a/examples/client_chat/client_chat.js b/examples/client_chat/client_chat.js index 884df18..de29fb7 100644 --- a/examples/client_chat/client_chat.js +++ b/examples/client_chat/client_chat.js @@ -17,7 +17,8 @@ const client = mc.createClient({ host, port, username, - auth: username === 'offline' ? 'offline' : 'microsoft' + auth: username === 'offline' ? 'offline' : 'microsoft', + version: '1.19.2' }) // Boilerplate diff --git a/src/client/chat.js b/src/client/chat.js index d942632..5a0cbe5 100644 --- a/src/client/chat.js +++ b/src/client/chat.js @@ -1,8 +1,12 @@ const states = require('../states') +const CHAT_PREVIEW_MODES = require('./chatPreviewModes') module.exports = (client, options) => { const mcData = require('minecraft-data')(client.version) const players = {} // 1.19+ + client.previewId = 0 + client.lastPreviewTime = 0 + client.msgAwaitingChatPreview = null function onChat (packet, isMessageFromServer = false) { let message = packet.message ?? packet.unsignedChatContent ?? packet.signedChatContent @@ -27,23 +31,99 @@ module.exports = (client, options) => { } }) - const EMPTY_BUFFER = Buffer.from([]) - client.on('state', function (newState) { if (newState === states.PLAY) { - client.chat = (message) => { - if (mcData.supportFeature('signedChat')) { - const timestamp = BigInt(Date.now()) - client.write('chat_message', { - message, - timestamp, - salt: 0, - signature: client.profileKeys != null ? EMPTY_BUFFER : client.signMessage(message, timestamp) - }) - } else { - client.write('chat', { message }) - } - } + client.chat = message => sendChatMessage(mcData, client, message) } }) } + +const EMPTY_BUFFER = Buffer.from([]) + +async function sendChatMessage (mcData, client, message) { + if (mcData.supportFeature('signedChat')) { + const timestamp = BigInt(Date.now()) + if (client.chatPreviewMode === CHAT_PREVIEW_MODES.OFF) { + console.log("server doesn't preview chat") + clearPreview(client) + } else { + if (message.startsWith('/')) { + // TODO: Implement + throw new Error('commands are unimplemented') + } else { + const normalizedMsg = trimAndRemoveRepeatedSpaces(message) + if (normalizedMsg.length !== 0 && client.lastPreviewedMsg !== normalizedMsg) { + client.lastPreviewedMsg = normalizedMsg + await requestChatPreview(client, message) + } else { + clearPreview(client) + } + // await preview reply + } + } + return + client.write('chat_message', { + message, + timestamp, + salt: 0, + signature: client.profileKeys != null ? EMPTY_BUFFER : client.signMessage(message, timestamp) + }) + } else { + client.write('chat', { message }) + } +} + +const { once } = require('events') + +async function requestChatPreview (client, msg) { + if (maybeTryPreviewMessage(client, msg)) { + console.log('did request') + client.msgAwaitingChatPreview = msg + const [data] = await once(client, 'chat_preview') + console.log('requested preview') + console.log(data) + } else { + console.log('didnt') + } +} + +function randomIntFromInterval (min, max) { // min and max included + return Math.floor(Math.random() * (max - min + 1) + min) +} + +// returns whether we previewed the message +function maybeTryPreviewMessage (client, msg) { + if (client.msgAwaitingChatPreview === msg) { + return true + } + + const nowMs = Date.now() + + if (nowMs >= client.lastPreviewTime + 100 && + (client.msgAwaitingChatPreview === null || nowMs >= client.lastPreviewTime + 1000)) { + client.lastPreviewTime = nowMs + client.previewId += randomIntFromInterval(1, 100) + console.log('sent request:', { + query: client.previewId, + message: msg + }) + client.write('chat_preview', { + query: client.previewId, + message: msg + }) + return true + } + return false +} + +function trimAndRemoveRepeatedSpaces (str) { + return str.trim().replace(/\s+/g, ' ') +} + +function clearPreview (client) { + client.lastPreviewedMsg = null + client.msgAwaitingChatPreview = null + client.lastPreviewResponse = null + // client.currentPreviewRequest = null + client.lastPreviewTime = 0 +} diff --git a/src/client/chatPreviewModes.js b/src/client/chatPreviewModes.js new file mode 100644 index 0000000..c14b174 --- /dev/null +++ b/src/client/chatPreviewModes.js @@ -0,0 +1,5 @@ +module.exports = { + OFF: 0, + LIVE: 1, + CONFIRM: 2 +} diff --git a/src/client/chat_activity_bring_to_mineflayer.js b/src/client/chat_activity_bring_to_mineflayer.js new file mode 100644 index 0000000..bb674d0 --- /dev/null +++ b/src/client/chat_activity_bring_to_mineflayer.js @@ -0,0 +1,24 @@ +const CHAT_PREVIEW_MODES = require('./chatPreviewModes') +const ping = require('../ping') + +module.exports = (client, options) => { + options.chatPreviewMode ??= CHAT_PREVIEW_MODES.LIVE // default in minecraft + + const mcData = require('minecraft-data')(client.version) + client.useChatPreview = false + client.chatPreviewMode = CHAT_PREVIEW_MODES.OFF + + // autoversion ping called too late + ping(options).then((reponse) => { + if (mcData.version['==']('1.19.2') && reponse.previewsChat && client.chatPreviewSetting !== CHAT_PREVIEW_MODES.OFF) { + console.log('enabling chat preview') + client.useChatPreview = true // default state before receiving packet + client.chatPreviewMode = options.chatPreviewMode + } + }).catch(() => { }) + + client.on('should_display_chat_preview', ({ should_display_chat_preview: shouldDisplayChatPreview }) => { + client.useChatPreview = shouldDisplayChatPreview + client.chatPreviewMode = client.useChatPreview ? options.chatPreviewMode : CHAT_PREVIEW_MODES.OFF + }) +} diff --git a/src/client/setProtocol.js b/src/client/setProtocol.js index 41ad123..c90317c 100644 --- a/src/client/setProtocol.js +++ b/src/client/setProtocol.js @@ -30,9 +30,10 @@ module.exports = function (client, options) { ? { timestamp: BigInt(client.profileKeys.expiresOn.getTime()), // should probably be called "expireTime" publicKey: client.profileKeys.publicDER, - signature: client.profileKeys.signature + signature: client.profileKeys.signatureV2 ?? client.profileKeys.signature } - : null + : null, + uuid: client.uuid }) } } diff --git a/src/createClient.js b/src/createClient.js index c64e17a..592e8a3 100644 --- a/src/createClient.js +++ b/src/createClient.js @@ -15,6 +15,7 @@ const autoVersion = require('./client/autoVersion') const pluginChannels = require('./client/pluginChannels') const versionChecking = require('./client/versionChecking') const chat = require('./client/chat') +const chat_activity = require('./client/chat_activity_bring_to_mineflayer') module.exports = createClient @@ -62,6 +63,7 @@ function createClient (options) { pluginChannels(client, options) versionChecking(client, options) chat(client, options) + chat_activity(client, options) return client }