diff --git a/index.html b/index.html index beddd643..ff559005 100644 --- a/index.html +++ b/index.html @@ -1,7 +1,7 @@ - Prismarine Viewer + Prismarine Web Client +
+
+

Welcome to prismarine-web-client! Chat appears here.

+
+
+
+
+ +
+
diff --git a/index.js b/index.js index 23ee193a..acc2a6c3 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,7 @@ process.versions.node = '14.0.0' const mineflayer = require('mineflayer') const { WorldView, Viewer } = require('prismarine-viewer/viewer') global.THREE = require('three') +const chat = require('./lib/chat') async function main () { const viewDistance = 6 @@ -42,6 +43,8 @@ async function main () { renderer.setSize(window.innerWidth, window.innerHeight) document.body.appendChild(renderer.domElement) + chat.init(undefined, bot._client, renderer) + // Create viewer const viewer = new Viewer(renderer) viewer.setVersion(version) diff --git a/lib/chat.js b/lib/chat.js new file mode 100644 index 00000000..4dbf54cf --- /dev/null +++ b/lib/chat.js @@ -0,0 +1,226 @@ +const styles = { + black: 'color:#000000', + dark_blue: 'color:#0000AA', + dark_green: 'color:#00AA00', + dark_aqua: 'color:#00AAAA', + dark_red: 'color:#AA0000', + dark_purple: 'color:#AA00AA', + gold: 'color:#FFAA00', + gray: 'color:#AAAAAA', + dark_gray: 'color:#555555', + blue: 'color:#5555FF', + green: 'color:#55FF55', + aqua: 'color:#55FFFF', + red: 'color:#FF5555', + light_purple: 'color:#FF55FF', + yellow: 'color:#FFFF55', + white: 'color:#FFFFFF', + bold: 'font-weight:900', + strikethrough: 'text-decoration:line-through', + underlined: 'text-decoration:underline', + italic: 'font-style:italic' +} +const dictionary = { + 'chat.stream.emote': '(%s) * %s %s', + 'chat.stream.text': '(%s) <%s> %s', + // 'chat.type.achievement': '%s has just earned the achievement %s', // 1.8? Not tested + // 'chat.type.advancement.task': '%s has just earned the advancement %s', + // 'chat.type.advancement.goal': '%s has just reached the goal %s', + // 'chat.type.advancement.challenge': '%s did a challenge lolol %s', + 'chat.type.admin': '[%s: %s]', + 'chat.type.announcement': '[%s] %s', + 'chat.type.emote': '* %s %s', + 'chat.type.text': '<%s> %s' +} + +export function init (mineweb, client, renderer) { + const chat = document.querySelector('#chat') + const chatInput = document.querySelector('#chatinput') + + const chatHistory = [] + let chatHistoryPos = 0 + + renderer.domElement.requestPointerLock = renderer.domElement.requestPointerLock || + renderer.domElement.mozRequestPointerLock || + renderer.domElement.webkitRequestPointerLock + + // Show chat + chat.style.display = 'block' + + let inChat = false + // Esc event - Doesnt work with onkeypress?! + document.onkeydown = function (e) { + if (!inChat) return + e = e || window.event + if (e.keyCode === 27 || e.key === 'Escape' || e.key === 'Esc') { + disableChat() + } else if (e.keyCode === 38) { + if (chatHistoryPos === 0) return + chatInput.value = chatHistory[--chatHistoryPos] !== undefined ? chatHistory[chatHistoryPos] : '' + } else if (e.keyCode === 40) { + if (chatHistoryPos === chatHistory.length) return + chatInput.value = chatHistory[++chatHistoryPos] !== undefined ? chatHistory[chatHistoryPos] : '' + } + } + + // Chat events + document.onkeypress = function (e) { + e = e || window.event + if (inChat === false) { + if (e.code === 'KeyT') { + enableChat(false) + } + + if (e.code === 'Slash') { + enableChat(true) + } + return false + } + + if (!inChat) return + e.stopPropagation() + if (e.code === 'Enter') { + chatHistory.push(chatInput.value) + /* TODO: mineweb._client */ client.write('chat', { + message: chatInput.value + }) + disableChat() + } + } + // Enable inputs back when focused + /* document.addEventListener("pointerlockchange", function(event) { + const canvas = document.getElementById("noa-canvas"); + if ( + document.pointerLockElement === canvas || + document.mozPointerLockElement === canvas + ) { + // Someone focused the game back so we hide chat. + inChat = false; + hideChat(); + } + }); */ + function enableChat (isCommand) { + // Set inChat value + inChat = true + // Exit the pointer lock + document.exitPointerLock() + // Show chat input + chatInput.style.display = 'block' + // Show extended chat history + chat.style.maxHeight = 'calc(90px * 8)' + chat.scrollTop = chat.scrollHeight // Stay bottom of the list + if (isCommand) { // handle commands + chatInput.value = '/' + } + // Focus element + chatInput.focus() + // Disable controls + // mineweb._noa.inputs.disabled = true; + chatHistoryPos = chatHistory.length + } + function disableChat () { + // Set inChat value + inChat = false + // Hide chat + hideChat() + + renderer.domElement.requestPointerLock() + // Enable controls + // mineweb._noa.inputs.disabled = false; + // Focus noa again + /* const canvas = document.getElementById("noa-canvas"); + canvas.requestPointerLock = + canvas.requestPointerLock || canvas.mozRequestPointerLock; + canvas.requestPointerLock(); */ + } + function hideChat () { + // Clear chat input + chatInput.value = '' + // Unfocus it + chatInput.blur() + // Hide it + chatInput.style.display = 'none' + // Hide extended chat history + chat.style.maxHeight = 'calc(90px * 4)' + chat.scrollTop = chat.scrollHeight // Stay bottom of the list + } + + function readExtra (extra) { + const shouldReturn = [] + for (const i in extra) { + if (extra[i].text) { + shouldReturn.push({ + text: extra[i].text, + color: extra[i].color, + bold: !!extra[i].bold, + italic: !!extra[i].italic, + underlined: !!extra[i].underlined, + strikethrough: !!extra[i].strikethrough, + obfuscated: !!extra[i].obfuscated + }) + } else { + readExtra(extra).forEach(function (el) { + shouldReturn.push(el) + }) + } + } + return shouldReturn + } + // Client part + /* TODO: mineweb._client */ client.on('chat', function (packet) { + // Reading of chat message + const fullmessage = JSON.parse(packet.message.toString()) + let msglist = [] + if ( + fullmessage.extra && + fullmessage.extra.length > 0 && + !fullmessage.translate + ) { + msglist = readExtra(fullmessage.extra) + } else if (fullmessage.text && fullmessage.text.length > 0) { + msglist.push({ text: fullmessage.text, color: undefined }) + } else if (dictionary[fullmessage.translate]) { + let msg = dictionary[fullmessage.translate] + fullmessage.with.forEach(obj => { + if (obj.insertion && obj.text) { + msg = msg.replace('%s', obj.text) + } + if (obj.extra) { + if (obj.text && obj.text.length > 0) { + msglist.push({ text: obj.text, color: undefined }) + } else { + const text = readExtra(obj.extra) + if (text.length > 1) { + console.log('Unsupported chat alert :(') + } + msg = msg.replace('%s', text[0].text) + msglist.push({ text: msg, color: undefined }) + } + } + }) + } else { + // msglist.push({ + // text: + // "Unsupported message (Please report this):\n" + + // JSON.stringify(fullmessage), + // color: undefined + // }); + } + const li = document.createElement('li') + msglist.forEach(msg => { + const span = document.createElement('span') + span.appendChild(document.createTextNode(msg.text)) + span.setAttribute( + 'style', + `${msg.color ? styles[msg.color.toLowerCase()] : styles.white}; ${ + msg.bold ? styles.bold + ';' : '' + }${msg.italic ? styles.italic + ';' : ''}${ + msg.strikethrough ? styles.strikethrough + ';' : '' + }${msg.underlined ? styles.underlined + ';' : ''}` + ) + li.appendChild(span) + }) + chat.appendChild(li) + chat.scrollTop = chat.scrollHeight // Stay bottom of the list + }) +} diff --git a/dns.js b/lib/dns.js similarity index 100% rename from dns.js rename to lib/dns.js diff --git a/perf_hooks_replacement.js b/lib/perf_hooks_replacement.js similarity index 100% rename from perf_hooks_replacement.js rename to lib/perf_hooks_replacement.js diff --git a/package.json b/package.json index 3668b3e8..3068e9ba 100644 --- a/package.json +++ b/package.json @@ -32,10 +32,10 @@ "homepage": "https://github.com/PrismarineJS/prismarine-web-client#readme", "dependencies": { "body-parser": "^1.19.0", + "compression": "^1.7.4", "express": "^4.17.1", "net-browserify": "^0.2.4", - "request": "^2.88.2", - "compression": "^1.7.4" + "request": "^2.88.2" }, "devDependencies": { "assert": "^2.0.0", diff --git a/webpack.config.js b/webpack.config.js index 1fc85d01..d39d47d8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -38,8 +38,8 @@ const config = { timers: require.resolve('timers-browserify'), // fs: require.resolve("fs-memory/singleton"), child_process: false, - perf_hooks: path.resolve(__dirname, 'perf_hooks_replacement.js'), - dns: path.resolve(__dirname, 'dns.js') + perf_hooks: path.resolve(__dirname, 'lib/perf_hooks_replacement.js'), + dns: path.resolve(__dirname, 'lib/dns.js') } }, plugins: [