From cc1a14ea4b6448a2c0639a5dc031e83602e503e9 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Sun, 14 Mar 2021 20:34:35 +0100 Subject: [PATCH] move chat to web component (#91) --- index.html | 11 +- index.js | 8 +- lib/chat.js | 395 ++++++++++++++++++++++++------------------ lib/loading_screen.js | 10 -- styles.css | 75 ++------ 5 files changed, 250 insertions(+), 249 deletions(-) diff --git a/index.html b/index.html index 462dd6f..1058eb2 100644 --- a/index.html +++ b/index.html @@ -7,16 +7,7 @@ - - + diff --git a/index.js b/index.js index 9717153..a60c59f 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ require('./lib/menu') require('./lib/loading_screen') require('./lib/hotbar') +require('./lib/chat') const net = require('net') @@ -13,7 +14,6 @@ const { WorldView, Viewer } = require('prismarine-viewer/viewer') const pathfinder = require('mineflayer-pathfinder') const { Vec3 } = require('vec3') global.THREE = require('three') -const Chat = require('./lib/chat') const maxPitch = 0.5 * Math.PI const minPitch = -0.5 * Math.PI @@ -25,8 +25,7 @@ async function main () { menu.style = 'display: none;' document.getElementById('hotbar').style = 'display:block' document.getElementById('crosshair').style = 'display:block' - document.getElementById('chat-wrapper').style = 'display:block' - document.getElementById('chat-wrapper2').style = 'display:block' + document.getElementById('chatbox').style = 'display:block' document.getElementById('loading-background').style = 'display:block' connect(options) @@ -36,6 +35,7 @@ async function main () { async function connect (options) { const loadingScreen = document.getElementById('loading-background') const hotbar = document.getElementById('hotbar') + const chat = document.getElementById('chatbox') const viewDistance = 6 const hostprompt = options.server @@ -119,7 +119,7 @@ async function connect (options) { renderer.setSize(window.innerWidth, window.innerHeight) document.body.appendChild(renderer.domElement) - const chat = Chat.init(bot._client, renderer) + chat.init(bot._client, renderer) // Create viewer const viewer = new Viewer(renderer) diff --git a/lib/chat.js b/lib/chat.js index 6cb196b..3eb90fd 100644 --- a/lib/chat.js +++ b/lib/chat.js @@ -1,3 +1,5 @@ +const { LitElement, html, css } = require('lit-element') + const styles = { black: 'color:#000000', dark_blue: 'color:#0000AA', @@ -33,193 +35,254 @@ const dictionary = { 'chat.type.text': '<%s> %s' } -export function init (client, renderer) { - const chat = document.querySelector('#chat') - const chatInput = document.querySelector('#chatinput') +class ChatBox extends LitElement { + static get styles () { + return css` + .chat-wrapper { + position: fixed; + background-color: rgba(0, 0, 0, 0.5); + z-index:10; + } - const chatHistory = [] - let chatHistoryPos = 0 + .chat-display-wrapper { + bottom: calc(8px * 16); + padding: 4px; + max-height: calc(90px * 8); + width: calc(320px * 4); + } - renderer.domElement.requestPointerLock = renderer.domElement.requestPointerLock || - renderer.domElement.mozRequestPointerLock || - renderer.domElement.webkitRequestPointerLock + .chat-input-wrapper { + bottom: calc(2px * 16); + width: 100%; + overflow: hidden; + background-color: rgba(0, 0, 0, 0); + } - // Show chat - chat.style.display = 'block' + .chat { + overflow: hidden; + color: white; + font-size: 16px; + margin: 0px; + line-height: 100%; + text-shadow: 2px 2px 0px #3f3f3f; + font-family: mojangles, minecraft, monospace; + width: 100%; + max-height: calc(90px * 8) + } - const chatState = { - inChat: false + input[type=text], #chatinput { + background-color: rgba(0, 0, 0, 0.5); + display: none; + } + + li { + display: block; + } + ` } - // Esc event - Doesnt work with onkeypress?! - document.onkeydown = function (e) { - if (!chatState.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] : '' + render () { + return html` +
+
+

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

+
+
+
+
+ +
+
+ ` + } + + init (client, renderer) { + this.inChat = false + const chat = this.shadowRoot.querySelector('#chat') + const chatInput = this.shadowRoot.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' + + const self = this + + // Esc event - Doesnt work with onkeypress?! + document.onkeydown = function (e) { + if (!self.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 (chatState.inChat === false) { - if (e.code === 'KeyT') { - enableChat(false) + // Chat events + document.onkeypress = function (e) { + e = e || window.event + if (self.inChat === false) { + if (e.code === 'KeyT') { + enableChat(false) + } + + if (e.code === 'Slash') { + enableChat(true) + } + return false } - if (e.code === 'Slash') { - enableChat(true) + if (!self.inChat) return + e.stopPropagation() + if (e.code === 'Enter') { + chatHistory.push(chatInput.value) + client.write('chat', { message: chatInput.value }) + disableChat() } - return false + } + // 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. + chatState.inChat = false; + hideChat(); + } + }); */ + function enableChat (isCommand) { + // Set inChat value + self.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() + chatHistoryPos = chatHistory.length } - if (!chatState.inChat) return - e.stopPropagation() - if (e.code === 'Enter') { - chatHistory.push(chatInput.value) - client.write('chat', { message: chatInput.value }) - disableChat() + function disableChat () { + // Set inChat value + self.inChat = false + + // Hide chat + hideChat() + + renderer.domElement.requestPointerLock() } - } - // 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. - chatState.inChat = false; - hideChat(); + + 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 enableChat (isCommand) { - // Set inChat value - chatState.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 = '/' + + 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 } - // Focus element - chatInput.focus() - chatHistoryPos = chatHistory.length - } - function disableChat () { - // Set inChat value - chatState.inChat = false - - // Hide chat - hideChat() - - renderer.domElement.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 + client.on('chat', (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 { - readExtra(extra).forEach(function (el) { - shouldReturn.push(el) - }) + // msglist.push({ + // text: + // "Unsupported message (Please report this):\n" + + // JSON.stringify(fullmessage), + // color: undefined + // }); } - } - return shouldReturn - } - - client.on('chat', (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 }) - } - } + 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) }) - } 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 }) - chat.appendChild(li) - chat.scrollTop = chat.scrollHeight // Stay bottom of the list - }) - hideChat() - - return chatState + hideChat() + } } + +window.customElements.define('chat-box', ChatBox) diff --git a/lib/loading_screen.js b/lib/loading_screen.js index 450f474..51370ff 100644 --- a/lib/loading_screen.js +++ b/lib/loading_screen.js @@ -34,16 +34,6 @@ class LoadingScreen extends LitElement { static get styles () { return css` - @font-face { - font-family: minecraft; - src: url(minecraftia.woff); - } - - @font-face { - font-family: mojangles; - src: url(mojangles.ttf); - } - h1 { font-family: mojangles, minecraft, monospace; } diff --git a/styles.css b/styles.css index e2fbf86..3b19594 100644 --- a/styles.css +++ b/styles.css @@ -1,22 +1,28 @@ -@font-face { - font-family: minecraft; - src: url(minecraftia.woff); -} - -@font-face { - font-family: mojangles; - src: url(mojangles.ttf); -} - html { height: 100%; overflow: hidden; } + +@font-face { + font-family: minecraft; + src: url(minecraftia.woff); +} + +@font-face { + font-family: mojangles; + src: url(mojangles.ttf); +} body { margin:0; padding:0; font-family: sans-serif; background: linear-gradient(#141e30, #243b55); + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } canvas { @@ -27,46 +33,6 @@ canvas { padding: 0; } -.chat-wrapper { - position: fixed; - background-color: rgba(0, 0, 0, 0.5); -} - -.chat-display-wrapper { - bottom: calc(8px * 16); - padding: 4px; - max-height: calc(90px * 8); - width: calc(320px * 4); -} - -.chat-input-wrapper { - bottom: calc(2px * 16); - width: 100%; - overflow: hidden; - background-color: rgba(0, 0, 0, 0); -} - -.chat { - overflow: hidden; - color: white; - font-size: 16px; - margin: 0px; - line-height: 100%; - text-shadow: 2px 2px 0px #3f3f3f; - font-family: mojangles, minecraft, monospace; - width: 100%; - max-height: calc(90px * 8) -} - -input[type=text], #chatinput { - background-color: rgba(0, 0, 0, 0.5); - display: none; -} - -li { - display: block; -} - #crosshair { image-rendering: optimizeSpeed; image-rendering: -moz-crisp-edges; @@ -82,12 +48,3 @@ li { transform: translate(calc(-50% + 120px * 4), calc(-50% + 120px * 4)); clip-path: inset(0px calc(240px * 4) calc(240px * 4) 0px); } - -body { - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -}