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: [