diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 542f704..0000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "html/hex-viewer"]
- path = html/hex-viewer
- url = https://github.com/Heath123/hex-viewer
diff --git a/html/hex-viewer b/html/hex-viewer
deleted file mode 160000
index 33738b7..0000000
--- a/html/hex-viewer
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 33738b7523da6bc5e823b8a446db734340f221fd
diff --git a/html/mainPage/hex-viewer.html b/html/mainPage/hex-viewer.html
new file mode 100644
index 0000000..8bf9c0a
--- /dev/null
+++ b/html/mainPage/hex-viewer.html
@@ -0,0 +1,89 @@
+
+
+
+
+
+ Packet hex
+
+
+
+ No packet selected.
+
+
+
diff --git a/html/mainPage/index.html b/html/mainPage/index.html
index 9415867..e07081a 100644
--- a/html/mainPage/index.html
+++ b/html/mainPage/index.html
@@ -89,8 +89,11 @@ Filtering
-
-
+
+
+
@@ -139,7 +142,7 @@
Scripting (beta)
-
+
diff --git a/html/mainPage/js/filteringLogic.js b/html/mainPage/js/filteringLogic.js
index 688411e..32796fb 100644
--- a/html/mainPage/js/filteringLogic.js
+++ b/html/mainPage/js/filteringLogic.js
@@ -1,7 +1,13 @@
exports.packetFilteredByFilterBox = function (packet, filter, hiddenPackets, inverseFiltering, regexFilter,
sharedVars) {
- if(!hiddenPackets) return false;
- if (hiddenPackets[packet.direction].includes(packet.meta.name)) {
+ if (!packet || !hiddenPackets) return false
+ const direction = packet.direction || ''
+ const meta = packet.meta || {}
+ const packetName = meta.name === undefined ? 'unknown' : String(meta.name)
+ const hiddenForDirection = Array.isArray(hiddenPackets[direction])
+ ? hiddenPackets[direction]
+ : []
+ if (hiddenForDirection.includes(packetName)) {
return true
}
@@ -9,7 +15,13 @@ exports.packetFilteredByFilterBox = function (packet, filter, hiddenPackets, inv
return false
}
- const comparisonString = packet.hexIdString + ' ' + packet.meta.name + ' ' + JSON.stringify(packet.data)
+ let packetData
+ try {
+ packetData = JSON.stringify(packet.data)
+ } catch (err) {
+ packetData = String(packet.data)
+ }
+ const comparisonString = String(packet.hexIdString || '') + ' ' + packetName + ' ' + packetData
if (regexFilter && typeof filter === 'string') {
try {
@@ -36,5 +48,5 @@ exports.packetFilteredByFilterBox = function (packet, filter, hiddenPackets, inv
}
exports.packetCollapsed = function (packet, filter, hiddenPackets) {
- return packet.meta.name === 'position'
+ return Boolean(packet && packet.meta && packet.meta.name === 'position')
}
diff --git a/html/mainPage/js/ipcHandler.js b/html/mainPage/js/ipcHandler.js
index 269d3bd..5373da8 100644
--- a/html/mainPage/js/ipcHandler.js
+++ b/html/mainPage/js/ipcHandler.js
@@ -5,15 +5,22 @@ exports.setup = function (passedSharedVars) {
sharedVars.ipcRenderer.on('copyPacketData', (event, arg) => {
const ipcMessage = JSON.parse(arg)
- let data = sharedVars.allPackets[ipcMessage.id].data
- data = sharedVars.proxyCapabilities.jsonData ? JSON.stringify(data, null, 2) : data.data
+ const packet = sharedVars.allPackets[Number(ipcMessage.id)]
+ if (!packet) return
+ const data = sharedVars.proxyCapabilities.jsonData
+ ? sharedVars.packetDom.serializeData(packet.data, 2)
+ : packet.data && packet.data.data !== undefined
+ ? String(packet.data.data)
+ : ''
sharedVars.ipcRenderer.send('copyToClipboard', data)
})
sharedVars.ipcRenderer.on('copyHexData', (event, arg) => {
const ipcMessage = JSON.parse(arg)
let data = ''
- for (const byte of sharedVars.allPackets[ipcMessage.id].raw) {
+ const packet = sharedVars.allPackets[Number(ipcMessage.id)]
+ if (!packet || !packet.raw) return
+ for (const byte of packet.raw) {
data += byte.toString(16).padStart(2, '0')
data += ' '
}
@@ -24,9 +31,11 @@ exports.setup = function (passedSharedVars) {
sharedVars.ipcRenderer.on('copyTeleportCommand', (event, arg) => {
console.log(sharedVars.allPackets)
const ipcMessage = JSON.parse(arg)
- const data = sharedVars.allPackets[ipcMessage.id].data
+ const packet = sharedVars.allPackets[Number(ipcMessage.id)]
+ if (!packet || !packet.data) return
+ const data = packet.data
- let clipData = '/tp @p ' + ((datasharedVars.packetsUpdated = false.flags & 0x01) ? '~' : '') + ((data.x === 0 && (data.flags & 0x01)) ? '' : data.x) +
+ let clipData = '/tp @p ' + ((data.flags & 0x01) ? '~' : '') + ((data.x === 0 && (data.flags & 0x01)) ? '' : data.x) +
((data.flags & 0x02) ? ' ~' : ' ') + ((data.y === 0 && (data.flags & 0x03)) ? '' : data.y) +
((data.flags & 0x04) ? ' ~' : ' ') + ((data.z === 0 && (data.flags & 0x04)) ? '' : data.z)
@@ -59,13 +68,127 @@ exports.setup = function (passedSharedVars) {
window.updateFilteringPackets()
})
- sharedVars.ipcRenderer.on('loadLogData', (event, arg) => {
- sharedVars.allPackets = JSON.parse(arg)
- for (const packet of sharedVars.allPackets) {
- sharedVars.packetDom.addPacketToDOM(packet)
+ sharedVars.ipcRenderer.on('loadLogStart', (event, request) => {
+ const { requestId, payload } = request
+
+ try {
+ window.activeLoadId = payload.loadId
+ window.loadLogRunning = true
+
+ if (typeof window.deselectPacket === 'function') {
+ window.deselectPacket()
+ }
+
+ sharedVars.allPackets = []
+ sharedVars.allPacketsHTML = []
+ sharedVars.hiddenPacketsAmount = 0
+ sharedVars.packetsUpdated = false
+ if (sharedVars.packetList) {
+ sharedVars.packetList.innerHTML = ''
+ }
+
+ console.log(
+ `Loading log: ${payload.filePath} (${payload.fileSize} bytes)`
+ )
+
+ sharedVars.ipcRenderer.send(
+ `loadLogStart-ack-${requestId}`,
+ {
+ success: true
+ }
+ )
+ } catch (err) {
+ sharedVars.ipcRenderer.send(
+ `loadLogStart-ack-${requestId}`,
+ {
+ error: err.message
+ }
+ )
}
})
+ sharedVars.ipcRenderer.on('loadLogChunk', (event, request) => {
+ const { requestId, payload } = request
+
+ try {
+ if (payload.loadId !== window.activeLoadId) {
+ throw new Error('Invalid load session')
+ }
+
+ sharedVars.packetDom.addPackets(payload.packets, true)
+
+ sharedVars.ipcRenderer.send(
+ `loadLogChunk-ack-${requestId}`,
+ {
+ success: true,
+ packetCount: sharedVars.allPackets.length
+ }
+ )
+ } catch (err) {
+ sharedVars.ipcRenderer.send(
+ `loadLogChunk-ack-${requestId}`,
+ {
+ error: err.message
+ }
+ )
+ }
+ })
+
+ sharedVars.ipcRenderer.on('loadLogFinish', (event, request) => {
+ const { requestId, payload } = request
+
+ try {
+ if (payload.loadId !== window.activeLoadId) {
+ throw new Error('Invalid load session')
+ }
+
+ sharedVars.packetDom.refresh()
+
+ console.log(
+ `Loaded ${payload.packetCount} packets`
+ )
+
+ window.activeLoadId = null
+ window.loadLogRunning = false
+
+ sharedVars.ipcRenderer.send(
+ `loadLogFinish-ack-${requestId}`,
+ {
+ success: true
+ }
+ )
+ } catch (err) {
+ sharedVars.ipcRenderer.send(
+ `loadLogFinish-ack-${requestId}`,
+ {
+ error: err.message
+ }
+ )
+ }
+ })
+
+ sharedVars.ipcRenderer.on('loadLogError', (event, payload) => {
+ if (
+ payload.loadId &&
+ window.activeLoadId &&
+ payload.loadId !== window.activeLoadId
+ ) {
+ return
+ }
+
+ window.activeLoadId = null
+ window.loadLogRunning = false
+
+ console.error(
+ 'Log load failed:',
+ payload.error
+ )
+
+ alert(
+ `Log load failed: ${payload.error}`
+ )
+ })
+
sharedVars.ipcRenderer.on('loadScriptData', (event, arg) => {
window.scriptEditor.getDoc().setValue(arg)
sharedVars.ipcRenderer.send('scriptStateChange', JSON.stringify({ //
@@ -79,9 +202,9 @@ exports.setup = function (passedSharedVars) {
document.getElementById('btnScriptSave').title = arg
})
-sharedVars.ipcRenderer.on('disableBtnScriptSave', (event, arg) => {
- document.getElementById('btnScriptSave').disabled = true
- document.getElementById('btnScriptSave').title = ''
-})
+ sharedVars.ipcRenderer.on('disableBtnScriptSave', (event, arg) => {
+ document.getElementById('btnScriptSave').disabled = true
+ document.getElementById('btnScriptSave').title = ''
+ })
}
diff --git a/html/mainPage/js/main.js b/html/mainPage/js/main.js
index 957d262..d222d95 100644
--- a/html/mainPage/js/main.js
+++ b/html/mainPage/js/main.js
@@ -21,11 +21,13 @@ function wrappedClusterizeUpdate (htmlArray) {
sharedVars.hiddenPacketsAmount = 0
const newArray = []
for (const item of htmlArray) {
+ const row = Array.isArray(item) ? item[0] : item
+ if (typeof row !== 'string') continue
// If the packet is hidden
- if (item[0].match(//)) {
+ if (row.match(//)) {
sharedVars.hiddenPacketsAmount += 1
} else {
- newArray.push(item)
+ newArray.push(row)
}
}
clusterize.update(newArray)
@@ -86,17 +88,20 @@ function updateFiltering () {
}
}
sharedVars.allPacketsHTML.forEach(function (item, index, array) {
- if (!filteringLogic.packetFilteredByFilterBox(sharedVars.allPackets[index],
+ const packet = sharedVars.allPackets[index]
+ const row = Array.isArray(item) ? item[0] : item
+ if (!packet || typeof row !== 'string') return
+ if (!filteringLogic.packetFilteredByFilterBox(packet,
regexFilter ? regex : sharedVars.lastFilter,
sharedVars.hiddenPackets,
inverseFiltering,
regexFilter,
sharedVars)) {
// If it's hidden, show it
- array[index] = [item[0].replace('filter-hidden', 'filter-shown')]
+ array[index] = [row.replace('filter-hidden', 'filter-shown')]
} else {
// If it's shown, hide it
- array[index] = [item[0].replace('filter-shown', 'filter-hidden')]
+ array[index] = [row.replace('filter-shown', 'filter-hidden')]
}
})
wrappedClusterizeUpdate(sharedVars.allPacketsHTML)
@@ -230,6 +235,16 @@ sharedVars.settings.setup(sharedVars)
// TODO: move to own file
const filteringPackets = document.getElementById('filtering-packets')
+const filteringSearch = document.getElementById('filtering-search')
+
+window.updateFilteringPacketSearch = function () {
+ const query = filteringSearch.value.trim().toLowerCase()
+
+ for (const item of filteringPackets.children) {
+ const matches = query === '' || item.textContent.toLowerCase().includes(query)
+ item.style.display = matches ? '' : 'none'
+ }
+}
function updateFilteringStorage () {
setVersionSpecificVar('hiddenPackets', sharedVars.hiddenPackets)
@@ -288,6 +303,7 @@ window.updateFilteringPackets = () => {
}
window.updateFilteringPackets()
+window.updateFilteringPacketSearch()
// Update every 0.05 seconds
// TODO: Find a better way without updating on every packet (which causes lag)
@@ -406,7 +422,7 @@ sharedVars.ipcRenderer.on('editAndResend', (event, arg) => {
})
function deselectPacket () {
- if (currentPacket) {
+ if (currentPacket !== undefined) {
removeOrAddSelection(currentPacket, false)
}
currentPacket = undefined
@@ -415,8 +431,12 @@ function deselectPacket () {
document.body.classList.remove('packetSelected')
document.body.classList.add('noPacketSelected')
hexViewer.style.display = 'none'
+ const copyButton = document.getElementById('copy-data-button')
+ if (copyButton) copyButton.disabled = true
}
+window.deselectPacket = deselectPacket
+
window.clearPackets = function () { // window. stops standardjs from complaining
deselectPacket()
sharedVars.allPackets = []
@@ -436,8 +456,14 @@ window.showAllPackets = function () { // window. stops standardjs from complaini
const hexViewer = document.getElementById('hex-viewer')
function removeOrAddSelection (id, add) {
+ if (!Number.isInteger(id) || !sharedVars.allPacketsHTML[id]) return false
+ const row = Array.isArray(sharedVars.allPacketsHTML[id])
+ ? sharedVars.allPacketsHTML[id][0]
+ : sharedVars.allPacketsHTML[id]
+ if (typeof row !== 'string') return false
const fakeElement = document.createElement('div')
- fakeElement.innerHTML = sharedVars.allPacketsHTML[id][0]
+ fakeElement.innerHTML = row
+ if (!fakeElement.firstChild) return false
if (add) {
fakeElement.firstChild.classList.add('selected')
} else {
@@ -447,9 +473,12 @@ function removeOrAddSelection (id, add) {
wrappedClusterizeUpdate(sharedVars.allPacketsHTML)
clusterize.refresh()
+ return true
}
window.packetClick = function (id) { // window. stops standardjs from complaining
+ if (!Number.isInteger(id) || !sharedVars.allPackets[id]) return
+ const packet = sharedVars.allPackets[id]
// Remove selection background from old selected packet
if (currentPacket !== undefined) {
removeOrAddSelection(currentPacket, false)
@@ -457,20 +486,22 @@ window.packetClick = function (id) { // window. stops standardjs from complainin
currentPacket = id
// const element = document.getElementById('packet' + id)
- currentPacketType = sharedVars.allPackets[id].name
+ currentPacketType = packet.name || (packet.meta && packet.meta.name)
removeOrAddSelection(currentPacket, true)
document.body.classList.remove('noPacketSelected')
document.body.classList.add('packetSelected')
if (sharedVars.proxyCapabilities.jsonData) {
// sidebar.innerHTML = 'Loading packet data...
';
- if (sharedVars.allPackets[id].data === undefined) {
+ if (packet.data === undefined) {
sharedVars.packetDom.getTreeElement().firstElementChild.innerHTML = 'Could not parse packet'
// TODO: Error message
} else {
- sharedVars.packetDom.getTree().loadData(sharedVars.allPackets[id].data)
+ sharedVars.packetDom.getTree().loadData(packet.data)
}
} else {
- sharedVars.packetDom.getTreeElement().innerText = sharedVars.allPackets[id].data.data
+ sharedVars.packetDom.getTreeElement().innerText = packet.data && packet.data.data !== undefined
+ ? packet.data.data
+ : 'Could not parse packet'
sharedVars.packetDom.getTreeElement().style = `
color: #0F0;
white-space: pre;
@@ -479,11 +510,14 @@ window.packetClick = function (id) { // window. stops standardjs from complainin
display: block;`
}
- if (sharedVars.proxyCapabilities.rawData) {
+ if (sharedVars.proxyCapabilities.rawData && packet.raw) {
hexViewer.style.display = 'block'
- hexViewer.contentWindow.postMessage(Buffer.from(sharedVars.allPackets[id].raw))
+ hexViewer.contentWindow.postMessage(Buffer.from(packet.raw), '*')
}
+ const copyButton = document.getElementById('copy-data-button')
+ if (copyButton) copyButton.disabled = false
+
scrollWikiToCurrentPacket()
}
@@ -663,15 +697,166 @@ function scrollWikiToCurrentPacket () {
}
}
}
+window.saveLogRunning = false
+
+async function saveLog () {
+ if (window.saveLogRunning) {
+ console.warn('Save is already running')
+ return
+ }
+
+ window.saveLogRunning = true
+
+ const CHUNK_SIZE = 2000
+ const saveButton = document.getElementById('save-log-button')
+
+ let sessionId = null
+ let finished = false
+
+ if (saveButton) {
+ saveButton.disabled = true
+ saveButton.innerText = 'Saving...'
+ }
+
+ try {
+ const startResult = await sharedVars.ipcRenderer.invoke(
+ 'startSaveLog'
+ )
+
+ if (startResult.canceled) {
+ return
+ }
+
+ if (startResult.busy) {
+ alert('A save operation is already running')
+ return
+ }
+
+ sessionId = startResult.sessionId
+
+ const totalPackets = sharedVars.allPackets.length
+
+ console.log(
+ `Starting save of ${totalPackets} packets`
+ )
+
+ for (
+ let index = 0;
+ index < totalPackets;
+ index += CHUNK_SIZE
+ ) {
+ const chunk = sharedVars.allPackets.slice(
+ index,
+ Math.min(index + CHUNK_SIZE, totalPackets)
+ )
+
+ const result = await sharedVars.ipcRenderer.invoke(
+ 'appendSaveLogChunk',
+ {
+ sessionId,
+ packets: chunk
+ }
+ )
+
+ const percent = totalPackets === 0
+ ? 100
+ : Math.floor(
+ result.packetCount / totalPackets * 100
+ )
+
+ if (saveButton) {
+ saveButton.innerText =
+ `Saving... ${percent}% (${result.packetCount}/${totalPackets})`
+ }
+
+ console.log(
+ `Saved ${result.packetCount}/${totalPackets}`
+ )
+ }
-function saveLog() {
- sharedVars.ipcRenderer.send('saveLog', JSON.stringify(sharedVars.allPackets))
+ const finishResult =
+ await sharedVars.ipcRenderer.invoke(
+ 'finishSaveLog',
+ {
+ sessionId
+ }
+ )
+
+ finished = true
+ sessionId = null
+
+ console.log(
+ `Saved ${finishResult.packetCount} packets to ${finishResult.filePath}`
+ )
+ } catch (err) {
+ console.error('Failed to save packet log:', err)
+
+ if (sessionId && !finished) {
+ try {
+ await sharedVars.ipcRenderer.invoke(
+ 'cancelSaveLog',
+ {
+ sessionId
+ }
+ )
+ } catch (cancelError) {
+ console.error(
+ 'Failed to cancel packet log save:',
+ cancelError
+ )
+ }
+ }
+
+ alert(`Failed to save packet log: ${err.message}`)
+ } finally {
+ window.saveLogRunning = false
+
+ if (saveButton) {
+ saveButton.disabled = false
+ saveButton.innerText = 'Save to file'
+ }
+ }
}
-function loadLog() {
- sharedVars.ipcRenderer.send('loadLog', '')
+window.loadLogRunning = false
+window.activeLoadId = null
+
+async function loadLog () {
+ if (window.loadLogRunning) {
+ console.warn('Load is already running')
+ return
+ }
+
+ window.loadLogRunning = true
+
+ try {
+ const result = await sharedVars.ipcRenderer.invoke(
+ 'startLoadLog'
+ )
+
+ if (result.canceled) {
+ window.loadLogRunning = false
+ return
+ }
+
+ if (result.busy) {
+ window.loadLogRunning = false
+ alert('A log load is already running')
+ return
+ }
+
+ window.activeLoadId = result.loadId
+ } catch (err) {
+ window.loadLogRunning = false
+ window.activeLoadId = null
+
+ console.error(err)
+ alert(`Could not load log: ${err.message}`)
+ }
}
+window.loadLog = loadLog
+
function saveScript( newfile = true ) {
if (newfile) {
sharedVars.ipcRenderer.send('saveAsScript', window.scriptEditor.getDoc().getValue())
@@ -682,4 +867,23 @@ function saveScript( newfile = true ) {
function loadScript() {
sharedVars.ipcRenderer.send('loadScript', '')
-}
\ No newline at end of file
+}
+
+window.copyCurrentPacketData = function () {
+ if (currentPacket === undefined || !sharedVars.allPackets[currentPacket]) return
+ const packet = sharedVars.allPackets[currentPacket]
+ const data = sharedVars.proxyCapabilities.jsonData
+ ? sharedVars.packetDom.serializeData(packet.data, 2)
+ : packet.data && packet.data.data !== undefined
+ ? String(packet.data.data)
+ : ''
+ sharedVars.ipcRenderer.send('copyToClipboard', data)
+ const copyButton = document.getElementById('copy-data-button')
+ if (copyButton) {
+ const originalText = copyButton.innerText
+ copyButton.innerText = 'Copied'
+ setTimeout(() => {
+ copyButton.innerText = originalText
+ }, 900)
+ }
+}
diff --git a/html/mainPage/js/packetDom.js b/html/mainPage/js/packetDom.js
index 6f37bca..4bc53dd 100644
--- a/html/mainPage/js/packetDom.js
+++ b/html/mainPage/js/packetDom.js
@@ -1,34 +1,55 @@
let tree
let treeElement
+let treeContent
let sharedVars
const filteringLogic = require('./filteringLogic.js')
-function trimData (data) { // Function to trim the size of stringified data for previews
- if (data === undefined) {
- // Undefined data, probably an invalid packet
+function stringifyData (data, spacing) {
+ const seen = new WeakSet()
+ try {
+ const result = JSON.stringify(data, (key, value) => {
+ if (typeof value === 'bigint') return `${value}n`
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
+ return Array.from(value)
+ }
+ if (value && typeof value === 'object') {
+ if (seen.has(value)) return '[Circular]'
+ seen.add(value)
+ }
+ return value
+ }, spacing)
+ return result === undefined ? String(data) : result
+ } catch (err) {
+ return String(data)
+ }
+}
+
+function trimData (data) {
+ if (data === undefined || data === null) {
return 'Could not parse packet'
}
let newData
if (sharedVars.proxyCapabilities.jsonData) {
- newData = Object.assign({}, data)
- Object.entries(newData).forEach(function (entry) {
- try {
- if (JSON.stringify(entry[1]).length > 15) {
- if (typeof entry[1] === 'number') {
- newData[entry[0]] = Math.round((entry[1] + Number.EPSILON) * 100) / 100
- } else {
- newData[entry[0]] = '...'
- }
+ const preview = {}
+ if (typeof data === 'object') {
+ Object.entries(data).forEach(([key, value]) => {
+ const serialized = stringifyData(value)
+ if (serialized && serialized.length > 15) {
+ preview[key] = typeof value === 'number'
+ ? Math.round((value + Number.EPSILON) * 100) / 100
+ : '...'
+ } else {
+ preview[key] = value
}
- } catch (err) {
-
- }
- })
- newData = JSON.stringify(newData)
+ })
+ newData = stringifyData(preview)
+ } else {
+ newData = String(data)
+ }
} else {
- newData = data.data
+ newData = data && data.data !== undefined ? String(data.data) : 'Could not parse packet'
}
if (newData.length > 750) {
newData = newData.slice(0, 750)
@@ -37,24 +58,35 @@ function trimData (data) { // Function to trim the size of stringified data for
}
function formatTime (ms) {
- // Based on https://stackoverflow.com/a/50409993/4012708
- return new Date(new Date(ms).getTime() - new Date().getTimezoneOffset() * 60000).toISOString().split("T")[1].replace(/[0-9]Z$/, '');
+ const date = new Date(ms)
+ if (!Number.isFinite(date.getTime())) return '--:--:--'
+ return new Date(date.getTime() - new Date().getTimezoneOffset() * 60000)
+ .toISOString().split('T')[1].replace(/[0-9]Z$/, '')
+}
+
+function packetHtml (packet, isHidden) {
+ const uid = packet.uid
+ const meta = packet.meta || {}
+ const id = packet.hexIdString === undefined ? '??' : String(packet.hexIdString)
+ const name = meta.name === undefined ? 'unknown' : String(meta.name)
+ const direction = packet.direction === undefined ? '' : String(packet.direction)
+ return `
+
+ ${escapeHtml(id)}
+ ${escapeHtml(name)}
+ ${escapeHtml(trimData(packet.data))}
+
+ ${escapeHtml(formatTime(packet.time))}
+ `
}
exports.addPacketToDOM = function (packet) {
+ if (!packet || typeof packet !== 'object') return false
const isHidden = filteringLogic.packetFilteredByFilterBox(packet, sharedVars.lastFilter, sharedVars.hiddenPackets,
// TODO: cache these?
sharedVars.settings.getSetting('inverseFiltering'), sharedVars.settings.getSetting('regexFilter'),
sharedVars)
- sharedVars.allPacketsHTML.push([
- `
-
- ${escapeHtml(packet.hexIdString)}
- ${escapeHtml(packet.meta.name)}
- ${escapeHtml(trimData(packet.data))}
-
- ${escapeHtml(formatTime(packet.time))}
- `])
+ sharedVars.allPacketsHTML.push([packetHtml(packet, isHidden)])
/* if (!noUpdate) {/html/mainPage/index.html/html/mainPage/index.html
clusterize.append(sharedVars.allPacketsHTML.slice(-1)[0]);
if (wasScrolledToBottom) {
@@ -67,6 +99,38 @@ exports.addPacketToDOM = function (packet) {
sharedVars.packetsUpdated = true
}
updateHidden()
+ return true
+}
+
+exports.addPackets = function (packets, deferRender) {
+ if (!Array.isArray(packets)) return 0
+ let added = 0
+ for (const data of packets) {
+ if (!data || typeof data !== 'object') continue
+ sharedVars.allPackets.push(data)
+ data.uid = sharedVars.allPackets.length - 1
+
+ const isHidden = filteringLogic.packetFilteredByFilterBox(
+ data,
+ sharedVars.lastFilter,
+ sharedVars.hiddenPackets,
+ sharedVars.settings.getSetting('inverseFiltering'),
+ sharedVars.settings.getSetting('regexFilter'),
+ sharedVars
+ )
+
+ sharedVars.allPacketsHTML.push([packetHtml(data, isHidden)])
+ added++
+
+ if (isHidden) {
+ sharedVars.hiddenPacketsAmount += 1
+ } else {
+ sharedVars.packetsUpdated = true
+ }
+ }
+
+ if (!deferRender) updateHidden()
+ return added
}
function refreshPackets () {
@@ -96,8 +160,14 @@ exports.setup = function (passedSharedVars) {
treeElement = document.getElementById('tree')
tree = jsonTree.create({}, treeElement)
+ treeContent = treeElement.firstElementChild
- treeElement.firstElementChild.innerHTML = 'No packet selected!'
+ treeContent.innerHTML = 'No packet selected!'
+
+ const actions = document.createElement('div')
+ actions.className = 'data-actions'
+ actions.innerHTML = ''
+ treeElement.appendChild(actions)
}
exports.addPacket = function (data) {
@@ -115,3 +185,10 @@ exports.getTreeElement = function () {
exports.getTree = function () {
return tree
}
+
+exports.serializeData = stringifyData
+
+exports.refresh = function () {
+ updateHidden()
+ sharedVars.packetsUpdated = true
+}
diff --git a/html/mainPage/style.css b/html/mainPage/style.css
index ecd2100..ec9c73e 100644
--- a/html/mainPage/style.css
+++ b/html/mainPage/style.css
@@ -1,318 +1,340 @@
-html {
- box-sizing: border-box;
- height: 100%;
-}
-
-body {
- height: calc(100% - 16px);
-}
-
-.topbar {
- height: 120px;
-}
-
-.container {
- height: calc(100% - 154px);
-}
-
-.split, .gutter.gutter-horizontal {
- float: left;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- height: 100%;
- /* Default before everything loads */
- width: calc(50% - 5px);
-}
-
-/* Targets the right panel to add spacing, but only before the gutter loads */
-#packets + #sidebar {
- margin-left: 10px;
-}
-
-.gutter.gutter-horizontal {
- cursor: ew-resize;
-}
-
-.box {
- overflow-y: auto;
- border: 1px solid #1A1A1A;
- border-radius: 10px;
- height: calc(100% - 24px); /* Take off toolbar height */
-}
-
-div.toolbar {
- height: 24px;
- overflow: hidden;
-}
-
-#packets {
- position: relative; /* Is this needed? TODO: Check */
-}
-
-.packetlist {
- list-style: none;
- padding: 0;
- margin: 0;
-}
-
-.packet:hover {
- background: #2C2C2C;
-}
-
-.packet {
- padding: 4px 8px;
- height: 22px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- color: rgba(255, 255, 255, 0.5);
-}
-
-.packet.filter-hidden {
- display: none;
-}
-
-.packet.serverbound::before {
- content: "▲";
- color: #00FF00;
- padding-right: 4px;
-}
-.packet.clientbound::before {
- content: "▼";
- color: #FF0000;
- padding-right: 4px;
-}
-
-.packet.invalid::before {
- color: #ffff00;
-}
-
-.name {
- color: rgba(255, 255, 255, 0.8);
-}
-
-body.noPacketSelected .whenPacketSelected {
- display: none;
-}
-
-body.packetSelected .whenNoPacketSelected {
- display: none;
-}
-
-.topbar-colour {
- top: 0;
- background: #121212;
- height: 40px;
- z-index: -1;
- position: fixed;
- width: 100%;
- left: 0;
-}
-
-span#hiddenPackets {
- color: rgba(255, 255, 255, 0.5);
-}
-
-.dialog-overlay {
- display: none;
-}
-
-.dialog-overlay.active {
- display: block;
- width: 100%;
- height: 100%;
- position: fixed;
- top: 0;
- left: 0;
- z-index: 99;
- background-color: rgba(0, 0, 0, 0.8);
-}
-
-div.dialog {
- position: fixed;
- top: 30px;
- left: 30px;
- background: #242424;
- height: calc(100% - 60px);
- width: calc(100% - 60px);
- border-radius: 20px;
- padding: 0 16px 16px 16px;
- box-sizing: border-box;
-}
-
-div.dialog-small {
- top: calc(50% - 100px);
- left: calc(50% - 250px);
- height: 200px;
- width: 500px;
-}
-
-div.dialog-medium {
- top: calc(50% - 170px);
- left: calc(50% - 250px);
- height: 340px;
- width: 500px;
-}
-
-.CodeMirror.CodeMirror {
- height: calc(100% - 120px);
-}
-
-div#Scripting .CodeMirror.CodeMirror {
- height: calc(100% - 185px);
-}
-
-/* TODO: needed? */
-.packetLink {
- text-decoration: none;
-}
-
-#tabcontent {
- box-sizing: border-box;
- position: fixed;
- height: calc(100% - 48px);
- width: calc(100% - 16px);
- z-index: 1;
- display: block;
- background: rgb(36, 36, 36);
-}
-
-/* Style the tab */
-.tab {
- overflow: hidden;
- /* border: 1px solid #ccc; */
- /* background-color: rgba(0, 0, 0, 0.2); */
-}
-
-/* Style the buttons inside the tab */
-.tab.tab button {
- margin: 0 2px;
- float: left;
- border: none;
- outline: none;
- cursor: pointer;
- font-size: 17px;
- background: #191919;
-}
-
-.tab-button {
- border-bottom-left-radius: 0;
- border-bottom-right-radius: 0;
- color: rgba(255, 255, 255, 0.8);
- background-color: inherit;
- padding: 6px 16px;
-}
-
-.back-button {
- padding: 4px 8px;
-}
-
-.tab.tab.toolbar button {
- background: #1e1e1e;
- padding: 3px 16px;
-}
-
-.tab.tab.toolbar:nth-child(1) {
- margin-left: 10px;
-}
-
-/* Change background color of buttons on hover */
-.tab button:hover {
- background-color: rgba(0, 0, 0, 0.35);
-}
-
-/* Create an active/current tablink class */
-.tab button.active {
- background-color: #242424;
-}
-
-.tab.toolbar button.active {
- background-color: rgba(0, 0, 0, 0.35);
-}
-
-.tab.toolbar button.active {
- background-color: rgba(0, 0, 0, 0.35);
-}
-
-
-/* Style the tab content */
-.tabcontent {
- height: 65px;
- display: none;
- padding: 6px 12px;
- /* border: 1px solid #ccc; */
- border-top: none;
-}
-
-.tabcontent.fullpage {
- position: relative;
- z-index: 1;
- background: #242424;
- height: calc(100vh - 52px);
-}
-
-div.search {
- height: 34px;
-}
-
-input.filter {
- box-sizing: border-box;
- height: 28px;
- width: 100%;
-}
-
-#iframe, #hex-viewer {
- border: 0;
- width: 100%;
- height: 100%;
-}
-
-div.error {
- position: fixed;
- right: 8px;
- top: 4px;
- text-align: right;
- font-size: 90%;
- color: red;
- user-select: text;
-}
-
-.packet.selected {
- background: rgba(64, 127, 255, 0.15);
-}
-
-#mainPresets, #extendedPresets {
- display: contents;
-}
-
-.error-dialog {
- user-select: text;
-}
-
-.bottom-button {
- position: absolute;
- bottom: 16px;
- user-select: none;
-}
-
-body.timeShown div.main-data {
- display: inline-block;
- width: calc(100% - 120px);
- overflow-x: hidden;
- text-overflow: ellipsis;
- /* Makes up for inline-block causing extra space on the bottom */
- margin-bottom: -4px;
-}
-
-body.timeNotShown div.main-data {
- display: inline;
-}
-
-body.timeNotShown span.time {
- display: none;
-}
-
-.settingDescription {
- color: rgba(255, 255, 255, 0.5);
-}
+html {
+ box-sizing: border-box;
+ height: 100%;
+}
+
+body {
+ height: calc(100% - 16px);
+}
+
+.topbar {
+ height: 120px;
+}
+
+.container {
+ height: calc(100% - 154px);
+}
+
+.split, .gutter.gutter-horizontal {
+ float: left;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ height: 100%;
+ /* Default before everything loads */
+ width: calc(50% - 5px);
+}
+
+/* Targets the right panel to add spacing, but only before the gutter loads */
+#packets + #sidebar {
+ margin-left: 10px;
+}
+
+.gutter.gutter-horizontal {
+ cursor: ew-resize;
+}
+
+.box {
+ overflow-y: auto;
+ border: 1px solid #1A1A1A;
+ border-radius: 10px;
+ height: calc(100% - 24px); /* Take off toolbar height */
+}
+
+div.toolbar {
+ height: 24px;
+ overflow: hidden;
+}
+
+#packets {
+ position: relative; /* Is this needed? TODO: Check */
+}
+
+.packetlist {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.packet:hover {
+ background: #2C2C2C;
+}
+
+.packet {
+ padding: 4px 8px;
+ height: 22px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.packet.filter-hidden {
+ display: none;
+}
+
+.packet.serverbound::before {
+ content: "▲";
+ color: #00FF00;
+ padding-right: 4px;
+}
+.packet.clientbound::before {
+ content: "▼";
+ color: #FF0000;
+ padding-right: 4px;
+}
+
+.packet.invalid::before {
+ color: #ffff00;
+}
+
+.name {
+ color: rgba(255, 255, 255, 0.8);
+}
+
+body.noPacketSelected .whenPacketSelected {
+ display: none;
+}
+
+body.packetSelected .whenNoPacketSelected {
+ display: none;
+}
+
+.topbar-colour {
+ top: 0;
+ background: #121212;
+ height: 40px;
+ z-index: -1;
+ position: fixed;
+ width: 100%;
+ left: 0;
+}
+
+span#hiddenPackets {
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.dialog-overlay {
+ display: none;
+}
+
+.dialog-overlay.active {
+ display: block;
+ width: 100%;
+ height: 100%;
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 99;
+ background-color: rgba(0, 0, 0, 0.8);
+}
+
+div.dialog {
+ position: fixed;
+ top: 30px;
+ left: 30px;
+ background: #242424;
+ height: calc(100% - 60px);
+ width: calc(100% - 60px);
+ border-radius: 20px;
+ padding: 0 16px 16px 16px;
+ box-sizing: border-box;
+}
+
+div.dialog-small {
+ top: calc(50% - 100px);
+ left: calc(50% - 250px);
+ height: 200px;
+ width: 500px;
+}
+
+div.dialog-medium {
+ top: calc(50% - 170px);
+ left: calc(50% - 250px);
+ height: 340px;
+ width: 500px;
+}
+
+.CodeMirror.CodeMirror {
+ height: calc(100% - 120px);
+}
+
+div#Scripting .CodeMirror.CodeMirror {
+ height: calc(100% - 185px);
+}
+
+/* TODO: needed? */
+.packetLink {
+ text-decoration: none;
+}
+
+#tabcontent {
+ box-sizing: border-box;
+ position: fixed;
+ height: calc(100% - 48px);
+ width: calc(100% - 16px);
+ z-index: 1;
+ display: block;
+ background: rgb(36, 36, 36);
+}
+
+/* Style the tab */
+.tab {
+ overflow: hidden;
+ /* border: 1px solid #ccc; */
+ /* background-color: rgba(0, 0, 0, 0.2); */
+}
+
+/* Style the buttons inside the tab */
+.tab.tab button {
+ margin: 0 2px;
+ float: left;
+ border: none;
+ outline: none;
+ cursor: pointer;
+ font-size: 17px;
+ background: #191919;
+}
+
+.tab-button {
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ color: rgba(255, 255, 255, 0.8);
+ background-color: inherit;
+ padding: 6px 16px;
+}
+
+.back-button {
+ padding: 4px 8px;
+}
+
+.tab.tab.toolbar button {
+ background: #1e1e1e;
+ padding: 3px 16px;
+}
+
+.tab.tab.toolbar:nth-child(1) {
+ margin-left: 10px;
+}
+
+/* Change background color of buttons on hover */
+.tab button:hover {
+ background-color: rgba(0, 0, 0, 0.35);
+}
+
+/* Create an active/current tablink class */
+.tab button.active {
+ background-color: #242424;
+}
+
+.tab.toolbar button.active {
+ background-color: rgba(0, 0, 0, 0.35);
+}
+
+.tab.toolbar button.active {
+ background-color: rgba(0, 0, 0, 0.35);
+}
+
+
+/* Style the tab content */
+.tabcontent {
+ height: 65px;
+ display: none;
+ padding: 6px 12px;
+ /* border: 1px solid #ccc; */
+ border-top: none;
+}
+
+.tabcontent.fullpage {
+ position: relative;
+ z-index: 1;
+ background: #242424;
+ height: calc(100vh - 52px);
+}
+
+div.search {
+ height: 34px;
+}
+
+input.filter {
+ box-sizing: border-box;
+ height: 28px;
+ width: 100%;
+}
+
+input.packet-type-filter {
+ margin-bottom: 8px;
+}
+
+#iframe, #hex-viewer {
+ border: 0;
+ width: 100%;
+ height: 100%;
+}
+#tree {
+ position: relative;
+}
+
+#tree .jsontree_tree {
+ padding-top: 38px;
+}
+
+.data-actions {
+ position: absolute;
+ top: 8px;
+ right: 10px;
+ z-index: 2;
+}
+
+.data-actions button {
+ padding: 4px 10px;
+}
+
+div.error {
+ position: fixed;
+ right: 8px;
+ top: 4px;
+ text-align: right;
+ font-size: 90%;
+ color: red;
+ user-select: text;
+}
+
+.packet.selected {
+ background: rgba(64, 127, 255, 0.15);
+}
+
+#mainPresets, #extendedPresets {
+ display: contents;
+}
+
+.error-dialog {
+ user-select: text;
+}
+
+.bottom-button {
+ position: absolute;
+ bottom: 16px;
+ user-select: none;
+}
+
+body.timeShown div.main-data {
+ display: inline-block;
+ width: calc(100% - 120px);
+ overflow-x: hidden;
+ text-overflow: ellipsis;
+ /* Makes up for inline-block causing extra space on the bottom */
+ margin-bottom: -4px;
+}
+
+body.timeNotShown div.main-data {
+ display: inline;
+}
+
+body.timeNotShown span.time {
+ display: none;
+}
+
+.settingDescription {
+ color: rgba(255, 255, 255, 0.5);
+}
diff --git a/package-lock.json b/package-lock.json
index 779c198..0b28797 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,7 @@
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
- "axios": "^0.21.1",
+ "axios": "^1.19.0",
"bedrock-protocol": "^3.52.0",
"clusterize.js": "^0.18.1",
"commander": "^7.1.0",
@@ -22,13 +22,13 @@
"electron-window-state": "^5.0.3",
"escape-html": "^1.0.3",
"md5-file": "^5.0.0",
- "minecraft-data": "^3.102.2",
+ "minecraft-data": "3.112.0",
"minecraft-folder-path": "^1.2.0",
- "minecraft-protocol": "^1.62.0",
+ "minecraft-protocol": "1.66.2",
"node-eval": "^2.0.0",
- "patch-package": "^6.4.7",
+ "patch-package": "^8.0.1",
"source-map-support": "^0.5.19",
- "ws": "^7.4.6"
+ "ws": "^7.5.13"
},
"devDependencies": {
"@electron-forge/cli": "^6.0.0-beta.61",
@@ -38,10 +38,6 @@
"@electron-forge/maker-zip": "^6.0.0-beta.61",
"electron": "^16.0.5",
"electron-rebuild": "^2.3.4"
- },
- "optionalDependencies": {
- "bufferutil": "^4.0.2",
- "utf-8-validate": "^5.0.3"
}
},
"node_modules/@azure/msal-common": {
@@ -503,7 +499,7 @@
"node_modules/@electron/node-gyp": {
"version": "10.2.0-electron.1",
"resolved": "git+ssh://git@github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2",
- "integrity": "sha512-7o2pP1DFdFgkNv5CxE4m3iLsf8Xa4pus2eD/yzjdHFzEuZMXOLv4nRpJEAvaLtbkRJHsuhRueIvVa4NFx78UgQ==",
+ "integrity": "sha512-4MSBTT8y07YUDqf69/vSh80Hh791epYqGtWHO3zSKhYFwQg+gx9wi1PqbqP6YqC4WMsNxZ5l9oDmnWdK5pfCKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -712,9 +708,9 @@
}
},
"node_modules/@electron/windows-sign/node_modules/fs-extra": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz",
- "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.6.tgz",
+ "integrity": "sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -989,9 +985,9 @@
}
},
"node_modules/@types/readable-stream": {
- "version": "4.0.23",
- "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.23.tgz",
- "integrity": "sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==",
+ "version": "4.0.24",
+ "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.24.tgz",
+ "integrity": "sha512-NRvUNC/JFGPJvqdAfEve8oginbM6V08u5NzLWpG8MwA2kTPOLnqk+wpwuPT+mp3aUsxyuT6m2gnrPuHYCruzEg==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
@@ -1072,7 +1068,6 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"debug": "4"
@@ -1249,13 +1244,13 @@
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
- "dev": true,
"license": "MIT"
},
"node_modules/at-least-node": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "dev": true,
"license": "ISC",
"engines": {
"node": ">= 4.0.0"
@@ -1298,12 +1293,31 @@
"license": "MIT"
},
"node_modules/axios": {
- "version": "0.21.4",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
- "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz",
+ "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==",
"license": "MIT",
"dependencies": {
- "follow-redirects": "^1.14.0"
+ "follow-redirects": "^1.16.0",
+ "form-data": "^4.0.6",
+ "https-proxy-agent": "^5.0.1",
+ "proxy-from-env": "^2.1.0"
+ }
+ },
+ "node_modules/axios/node_modules/form-data": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
+ "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.4",
+ "mime-types": "^2.1.35"
+ },
+ "engines": {
+ "node": ">= 6"
}
},
"node_modules/balanced-match": {
@@ -1343,9 +1357,9 @@
}
},
"node_modules/bedrock-protocol": {
- "version": "3.56.1",
- "resolved": "https://registry.npmjs.org/bedrock-protocol/-/bedrock-protocol-3.56.1.tgz",
- "integrity": "sha512-H/rd6PVcYxu+w2bEN3e5XoPGGsHA7v2atfh8Bl436KXRXWvGlMoRBPj548bgNhyxewgxCANJxE+MPJP4RWyYYA==",
+ "version": "3.57.0",
+ "resolved": "https://registry.npmjs.org/bedrock-protocol/-/bedrock-protocol-3.57.0.tgz",
+ "integrity": "sha512-+4YyRvDD4Wf5g8ZInxjJRoVySDJBQKy0atHBGcQqmrwPJ4DQWrlj3DavQTt78cw3iPZMTQ3ZgKvWMGfnv+jilg==",
"license": "MIT",
"dependencies": {
"debug": "^4.3.1",
@@ -1438,9 +1452,9 @@
"optional": true
},
"node_modules/brace-expansion": {
- "version": "1.1.14",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
- "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
+ "version": "1.1.16",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
+ "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
@@ -1541,20 +1555,6 @@
"node": ">=0.2.0"
}
},
- "node_modules/bufferutil": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.1.0.tgz",
- "integrity": "sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==",
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "node-gyp-build": "^4.3.0"
- },
- "engines": {
- "node": ">=6.14.2"
- }
- },
"node_modules/cacache": {
"version": "16.1.3",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz",
@@ -1666,6 +1666,53 @@
"node": ">=8"
}
},
+ "node_modules/call-bind": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz",
+ "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "get-intrinsic": "^1.3.0",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/camelcase": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
@@ -1731,10 +1778,19 @@
}
},
"node_modules/ci-info": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
- "license": "MIT"
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+ "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
},
"node_modules/clean-stack": {
"version": "2.2.0",
@@ -1896,6 +1952,15 @@
"readable-stream": "^2.0.0 || ^1.1.13"
}
},
+ "node_modules/cmake-js/node_modules/axios": {
+ "version": "0.21.4",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+ "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.14.0"
+ }
+ },
"node_modules/cmake-js/node_modules/chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
@@ -2141,7 +2206,6 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
@@ -2252,7 +2316,6 @@
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
@@ -2464,9 +2527,7 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
- "dev": true,
"license": "MIT",
- "optional": true,
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
@@ -2502,7 +2563,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.4.0"
@@ -2564,6 +2624,20 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/duplexer2": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
@@ -2859,9 +2933,9 @@
}
},
"node_modules/electron-installer-debian/node_modules/yargs": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
- "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "version": "16.2.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.2.tgz",
+ "integrity": "sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -3010,9 +3084,9 @@
}
},
"node_modules/electron-installer-redhat/node_modules/yargs": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
- "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "version": "16.2.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.2.tgz",
+ "integrity": "sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -3124,9 +3198,9 @@
}
},
"node_modules/electron-packager/node_modules/fs-extra": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz",
- "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.6.tgz",
+ "integrity": "sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3300,9 +3374,9 @@
}
},
"node_modules/electron-rebuild/node_modules/yargs": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
- "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "version": "16.2.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.2.tgz",
+ "integrity": "sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3391,9 +3465,9 @@
}
},
"node_modules/electron-winstaller": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz",
- "integrity": "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==",
+ "version": "5.4.4",
+ "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.4.tgz",
+ "integrity": "sha512-j9ETcBGJaXxAY/b6UBpR7LZfjdU4BAO+yvr4ifqHEdyuc3UNCy91PDGkWKY5UQ4coHNYfnwFggrqD6QPeFGAlg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -3403,6 +3477,7 @@
"debug": "^4.1.1",
"fs-extra": "^7.0.1",
"lodash": "^4.17.21",
+ "semver": "^7.6.3",
"temp": "^0.9.0"
},
"engines": {
@@ -3744,9 +3819,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
- "dev": true,
"license": "MIT",
- "optional": true,
"engines": {
"node": ">= 0.4"
}
@@ -3755,12 +3828,38 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
+ "node_modules/es-object-atoms": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/es6-error": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
@@ -4022,9 +4121,9 @@
"license": "MIT"
},
"node_modules/fast-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
- "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz",
+ "integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==",
"funding": [
{
"type": "github",
@@ -4192,7 +4291,6 @@
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
@@ -4207,7 +4305,6 @@
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
"integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
@@ -4255,7 +4352,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -4366,6 +4462,30 @@
"global-modules": "1.0.0"
}
},
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/get-package-info": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz",
@@ -4399,6 +4519,19 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
@@ -4460,9 +4593,9 @@
}
},
"node_modules/glob/node_modules/brace-expansion": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
- "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz",
+ "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4585,9 +4718,7 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
- "dev": true,
"license": "MIT",
- "optional": true,
"engines": {
"node": ">= 0.4"
},
@@ -4689,9 +4820,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
- "dev": true,
"license": "MIT",
- "optional": true,
"dependencies": {
"es-define-property": "^1.0.0"
},
@@ -4699,6 +4828,33 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
@@ -4706,10 +4862,9 @@
"license": "ISC"
},
"node_modules/hasown": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
- "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
- "dev": true,
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
@@ -4794,7 +4949,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "6",
@@ -4942,18 +5096,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/is-ci": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
- "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
- "license": "MIT",
- "dependencies": {
- "ci-info": "^2.0.0"
- },
- "bin": {
- "is-ci": "bin.js"
- }
- },
"node_modules/is-core-module": {
"version": "2.16.2",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz",
@@ -5176,6 +5318,31 @@
"integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==",
"license": "BSD-2-Clause"
},
+ "node_modules/json-stable-stringify": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz",
+ "integrity": "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "isarray": "^2.0.5",
+ "jsonify": "^0.0.1",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/json-stable-stringify/node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "license": "MIT"
+ },
"node_modules/json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@@ -5192,6 +5359,15 @@
"graceful-fs": "^4.1.6"
}
},
+ "node_modules/jsonify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz",
+ "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==",
+ "license": "Public Domain",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/jsonwebtoken": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
@@ -5666,6 +5842,15 @@
"node": ">=10"
}
},
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/md5-file": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/md5-file/-/md5-file-5.0.0.tgz",
@@ -5763,7 +5948,6 @@
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -5773,7 +5957,6 @@
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
@@ -5802,9 +5985,9 @@
}
},
"node_modules/minecraft-data": {
- "version": "3.110.2",
- "resolved": "https://registry.npmjs.org/minecraft-data/-/minecraft-data-3.110.2.tgz",
- "integrity": "sha512-u0aCCSpQWVreGnZGU/Lu0jmZmc0Y37M0Fvw6eQVQY0BdS/BGRDDU+ug6/qP3QDuZRJCSzi8wNW8ODnOhwpnkpA==",
+ "version": "3.112.0",
+ "resolved": "https://registry.npmjs.org/minecraft-data/-/minecraft-data-3.112.0.tgz",
+ "integrity": "sha512-U+BJ+3zFcTmi6X936sKNBRgocr17t7poNg40+yE3ng7B0wNWwqpndzqJ4cGfjBcclgFghEoOyXvcgIS4MPDRYQ==",
"license": "MIT"
},
"node_modules/minecraft-folder-path": {
@@ -6126,12 +6309,13 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true,
"license": "MIT"
},
"node_modules/node-abi": {
- "version": "3.92.0",
- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz",
- "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==",
+ "version": "3.94.0",
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.94.0.tgz",
+ "integrity": "sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6215,18 +6399,6 @@
"node": ">= 10.12.0"
}
},
- "node_modules/node-gyp-build": {
- "version": "4.8.4",
- "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
- "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
- "license": "MIT",
- "optional": true,
- "bin": {
- "node-gyp-build": "bin.js",
- "node-gyp-build-optional": "optional.js",
- "node-gyp-build-test": "build-test.js"
- }
- },
"node_modules/node-gyp/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -6593,9 +6765,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true,
"license": "MIT",
- "optional": true,
"engines": {
"node": ">= 0.4"
}
@@ -6722,6 +6892,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -6873,128 +7044,34 @@
}
},
"node_modules/patch-package": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.5.1.tgz",
- "integrity": "sha512-I/4Zsalfhc6bphmJTlrLoOcAF87jcxko4q0qsv4bGcurbr8IskEOtdnt9iCmsQVGL1B+iUhSQqweyTLJfCF9rA==",
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz",
+ "integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==",
"license": "MIT",
"dependencies": {
"@yarnpkg/lockfile": "^1.1.0",
"chalk": "^4.1.2",
- "cross-spawn": "^6.0.5",
+ "ci-info": "^3.7.0",
+ "cross-spawn": "^7.0.3",
"find-yarn-workspace-root": "^2.0.0",
- "fs-extra": "^9.0.0",
- "is-ci": "^2.0.0",
+ "fs-extra": "^10.0.0",
+ "json-stable-stringify": "^1.0.2",
"klaw-sync": "^6.0.0",
"minimist": "^1.2.6",
"open": "^7.4.2",
- "rimraf": "^2.6.3",
- "semver": "^5.6.0",
+ "semver": "^7.5.3",
"slash": "^2.0.0",
- "tmp": "^0.0.33",
- "yaml": "^1.10.2"
+ "tmp": "^0.2.4",
+ "yaml": "^2.2.2"
},
"bin": {
"patch-package": "index.js"
},
"engines": {
- "node": ">=10",
+ "node": ">=14",
"npm": ">5"
}
},
- "node_modules/patch-package/node_modules/cross-spawn": {
- "version": "6.0.6",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz",
- "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==",
- "license": "MIT",
- "dependencies": {
- "nice-try": "^1.0.4",
- "path-key": "^2.0.1",
- "semver": "^5.5.0",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- },
- "engines": {
- "node": ">=4.8"
- }
- },
- "node_modules/patch-package/node_modules/fs-extra": {
- "version": "9.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
- "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
- "license": "MIT",
- "dependencies": {
- "at-least-node": "^1.0.0",
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/patch-package/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/patch-package/node_modules/path-key": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/patch-package/node_modules/semver": {
- "version": "5.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
- "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver"
- }
- },
- "node_modules/patch-package/node_modules/shebang-command": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
- "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
- "license": "MIT",
- "dependencies": {
- "shebang-regex": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/patch-package/node_modules/shebang-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
- "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/patch-package/node_modules/which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
- "license": "ISC",
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "which": "bin/which"
- }
- },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -7018,7 +7095,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -7357,6 +7433,7 @@
"resolved": "https://registry.npmjs.org/prismarine-registry/-/prismarine-registry-1.12.0.tgz",
"integrity": "sha512-OC5U6YrflY6OcAWRZEqe2HGZuNp0bIuP7H+oKEHD6rLfKNDxo8Ymx5eh2VvrZWnMVugpwID1Qj/UjA4MoCzNDw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"minecraft-data": "^3.70.0",
"prismarine-block": "^1.17.1",
@@ -7524,6 +7601,15 @@
"safe-buffer": "~5.2.0"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+ "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/psl": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
@@ -8134,9 +8220,9 @@
}
},
"node_modules/semver": {
- "version": "7.8.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz",
- "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -8191,6 +8277,23 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
@@ -8215,7 +8318,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
@@ -8228,7 +8330,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -8687,15 +8788,12 @@
"optional": true
},
"node_modules/tmp": {
- "version": "0.0.33",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
- "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz",
+ "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==",
"license": "MIT",
- "dependencies": {
- "os-tmpdir": "~1.0.2"
- },
"engines": {
- "node": ">=0.6.0"
+ "node": ">=14.14"
}
},
"node_modules/tmp-promise": {
@@ -8709,17 +8807,6 @@
"tmp": "^0.2.0"
}
},
- "node_modules/tmp-promise/node_modules/tmp": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
- "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=14.14"
- }
- },
"node_modules/to-readable-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz",
@@ -8991,20 +9078,6 @@
"node": ">=8"
}
},
- "node_modules/utf-8-validate": {
- "version": "5.0.10",
- "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
- "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "node-gyp-build": "^4.3.0"
- },
- "engines": {
- "node": ">=6.14.2"
- }
- },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -9093,7 +9166,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
@@ -9186,9 +9258,9 @@
"license": "ISC"
},
"node_modules/ws": {
- "version": "7.5.10",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
- "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
+ "version": "7.5.13",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.13.tgz",
+ "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==",
"license": "MIT",
"engines": {
"node": ">=8.3.0"
@@ -9234,18 +9306,24 @@
"license": "ISC"
},
"node_modules/yaml": {
- "version": "1.10.3",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
- "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
+ "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
"license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
"engines": {
- "node": ">= 6"
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
}
},
"node_modules/yargs": {
- "version": "17.7.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
- "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "version": "17.7.3",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz",
+ "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==",
"dev": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index a674e8b..1e3ff69 100644
--- a/package.json
+++ b/package.json
@@ -43,28 +43,24 @@
"electron-rebuild": "^2.3.4"
},
"dependencies": {
- "axios": "^0.21.1",
+ "axios": "^1.19.0",
"bedrock-protocol": "^3.52.0",
"clusterize.js": "^0.18.1",
"commander": "^7.1.0",
+ "electron-fetch": "1.9.1",
"electron-localshortcut": "^3.2.1",
"electron-squirrel-startup": "^1.0.0",
"electron-store": "^8.0.1",
"electron-unhandled": "^3.0.2",
"electron-window-state": "^5.0.3",
- "electron-fetch": "1.9.1",
"escape-html": "^1.0.3",
"md5-file": "^5.0.0",
- "minecraft-data": "^3.102.2",
+ "minecraft-data": "3.112.0",
"minecraft-folder-path": "^1.2.0",
- "minecraft-protocol": "^1.62.0",
+ "minecraft-protocol": "1.66.2",
"node-eval": "^2.0.0",
- "patch-package": "^6.4.7",
+ "patch-package": "^8.0.1",
"source-map-support": "^0.5.19",
- "ws": "^7.4.6"
- },
- "optionalDependencies": {
- "bufferutil": "^4.0.2",
- "utf-8-validate": "^5.0.3"
+ "ws": "^7.5.13"
}
}
diff --git a/patches/minecraft-data+3.112.0.patch b/patches/minecraft-data+3.112.0.patch
new file mode 100644
index 0000000..cb3151c
--- /dev/null
+++ b/patches/minecraft-data+3.112.0.patch
@@ -0,0 +1,28 @@
+diff --git a/node_modules/minecraft-data/index.js b/node_modules/minecraft-data/index.js
+index 526dba1..8316f66 100644
+--- a/node_modules/minecraft-data/index.js
++++ b/node_modules/minecraft-data/index.js
+@@ -56,6 +56,11 @@ module.exports = function (mcVersion, preNetty) {
+ preNetty = preNetty || false
+ mcVersion = String(mcVersion).replace('pe_', 'bedrock_')
+
++ // Minecraft 26.2 is present in the protocol version index before its
++ // complete generated data is published. Reuse the 26.1 schema until then.
++ const compatibilityVersion = mcVersion === '26.2'
++ if (compatibilityVersion) mcVersion = '26.1'
++
+ const majorVersion = toMajor(mcVersion, preNetty)
+ if (majorVersion == null) { return null }
+ const cachedName = `${majorVersion.type}_${majorVersion.majorVersion}_${majorVersion.dataVersion}`
+@@ -67,6 +72,11 @@ module.exports = function (mcVersion, preNetty) {
+ nmcData.isNewerOrEqualTo = version => nmcData.version['>='](version)
+ nmcData.isOlderThan = version => nmcData.version['<'](version)
+ nmcData.version = Object.assign(majorVersion, nmcData.version)
++ if (compatibilityVersion) {
++ nmcData.version.minecraftVersion = '26.2'
++ nmcData.version.majorVersion = '26.2'
++ nmcData.version.version = 776
++ }
+ cache[cachedName] = nmcData
+ nmcData.supportFeature = supportFeature(nmcData.version, protocolVersions[nmcData.type])
+ return nmcData
diff --git a/patches/minecraft-protocol+1.25.0.patch b/patches/minecraft-protocol+1.25.0.patch
deleted file mode 100644
index cdd0da1..0000000
--- a/patches/minecraft-protocol+1.25.0.patch
+++ /dev/null
@@ -1,12 +0,0 @@
-diff --git a/node_modules/minecraft-protocol/src/client/encrypt.js b/node_modules/minecraft-protocol/src/client/encrypt.js
-index d66f1d1..3cad7e4 100644
---- a/node_modules/minecraft-protocol/src/client/encrypt.js
-+++ b/node_modules/minecraft-protocol/src/client/encrypt.js
-@@ -22,6 +22,7 @@ module.exports = function (client, options) {
- joinServerRequest(onJoinServerResponse)
- } else {
- if (packet.serverId !== '-') {
-+ client.emit('noAuth')
- debug('This server appears to be an online server and you are providing no password, the authentication will probably fail')
- }
- sendEncryptionKeyResponse()
diff --git a/patches/minecraft-protocol+1.66.2.patch b/patches/minecraft-protocol+1.66.2.patch
new file mode 100644
index 0000000..79e1c2a
--- /dev/null
+++ b/patches/minecraft-protocol+1.66.2.patch
@@ -0,0 +1,61 @@
+diff --git a/node_modules/minecraft-protocol/src/client.js b/node_modules/minecraft-protocol/src/client.js
+--- a/node_modules/minecraft-protocol/src/client.js
++++ b/node_modules/minecraft-protocol/src/client.js
+@@ -53,6 +53,7 @@ class Client extends EventEmitter {
+ })
+
+ this.splitter.recognizeLegacyPing = state === states.HANDSHAKING
++ let deserializerErrorReported = false
+
+ this.serializer.on('error', (e) => {
+ let parts
+@@ -68,6 +69,8 @@ class Client extends EventEmitter {
+ })
+
+ this.deserializer.on('error', (e) => {
++ if (deserializerErrorReported) return
++ deserializerErrorReported = true
+ let parts = []
+ if (e.field) {
+ parts = e.field.split('.')
+diff --git a/node_modules/minecraft-protocol/src/client/encrypt.js b/node_modules/minecraft-protocol/src/client/encrypt.js
+index 63cc2bd..1b95f5d 100644
+--- a/node_modules/minecraft-protocol/src/client/encrypt.js
++++ b/node_modules/minecraft-protocol/src/client/encrypt.js
+@@ -23,6 +23,7 @@ module.exports = function (client, options) {
+ joinServerRequest(onJoinServerResponse)
+ } else {
+ if (packet.serverId !== '-') {
++ client.emit('noAuth')
+ debug('This server appears to be an online server and you are providing no password, the authentication will probably fail')
+ }
+ sendEncryptionKeyResponse()
+diff --git a/node_modules/minecraft-protocol/src/server/login.js b/node_modules/minecraft-protocol/src/server/login.js
+index ec40ed9..9f7cad2 100644
+--- a/node_modules/minecraft-protocol/src/server/login.js
++++ b/node_modules/minecraft-protocol/src/server/login.js
+@@ -223,6 +223,13 @@ module.exports = function (client, server, options) {
+
+ function onClientLoginAck () {
+ client.state = states.CONFIGURATION
++ if (options.disableDefaultConfiguration) {
++ client.once('finish_configuration', () => {
++ client.state = states.PLAY
++ server.emit('playerJoin', client)
++ })
++ return
++ }
+ if (client.supportFeature('segmentedRegistryCodecData')) {
+ for (const key in options.registryCodec) {
+ const entry = options.registryCodec[key]
+diff --git a/node_modules/minecraft-protocol/src/version.js b/node_modules/minecraft-protocol/src/version.js
+index 51312c2..5ccad63 100644
+--- a/node_modules/minecraft-protocol/src/version.js
++++ b/node_modules/minecraft-protocol/src/version.js
+@@ -2,5 +2,5 @@
+
+ module.exports = {
+ defaultVersion: '1.21.11',
+- supportedVersions: ['1.7', '1.8.8', '1.9.4', '1.10.2', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4', '1.20', '1.20.1', '1.20.2', '1.20.4', '1.20.6', '1.21.1', '1.21.3', '1.21.4', '1.21.5', '1.21.6', '1.21.8', '1.21.9', '1.21.11']
++ supportedVersions: ['1.7', '1.8.8', '1.9.4', '1.10.2', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4', '1.20', '1.20.1', '1.20.2', '1.20.4', '1.20.6', '1.21.1', '1.21.3', '1.21.4', '1.21.5', '1.21.6', '1.21.8', '1.21.9', '1.21.11', '26.2']
+ }
diff --git a/patches/prismarine-auth+2.7.0.patch b/patches/prismarine-auth+2.7.0.patch
deleted file mode 100644
index 7662781..0000000
--- a/patches/prismarine-auth+2.7.0.patch
+++ /dev/null
@@ -1,79 +0,0 @@
-diff --git a/node_modules/prismarine-auth/src/TokenManagers/LiveTokenManager.js b/node_modules/prismarine-auth/src/TokenManagers/LiveTokenManager.js
-index 062b3c0..7c714ec 100644
---- a/node_modules/prismarine-auth/src/TokenManagers/LiveTokenManager.js
-+++ b/node_modules/prismarine-auth/src/TokenManagers/LiveTokenManager.js
-@@ -1,5 +1,5 @@
- const debug = require('debug')('prismarine-auth')
--
-+const fetch = require('electron-fetch').default;
- const { Endpoints } = require('../common/Constants')
- const { checkStatus } = require('../common/Util')
-
-diff --git a/node_modules/prismarine-auth/src/TokenManagers/MinecraftBedrockServicesManager.js b/node_modules/prismarine-auth/src/TokenManagers/MinecraftBedrockServicesManager.js
-index 06f9ed7..e0d168f 100644
---- a/node_modules/prismarine-auth/src/TokenManagers/MinecraftBedrockServicesManager.js
-+++ b/node_modules/prismarine-auth/src/TokenManagers/MinecraftBedrockServicesManager.js
-@@ -1,5 +1,5 @@
- const debug = require('debug')('prismarine-auth')
--
-+const fetch = require('electron-fetch').default;
- const { Endpoints } = require('../common/Constants')
- const { checkStatus } = require('../common/Util')
-
-diff --git a/node_modules/prismarine-auth/src/TokenManagers/MinecraftBedrockTokenManager.js b/node_modules/prismarine-auth/src/TokenManagers/MinecraftBedrockTokenManager.js
-index 9c5036e..a91a61d 100644
---- a/node_modules/prismarine-auth/src/TokenManagers/MinecraftBedrockTokenManager.js
-+++ b/node_modules/prismarine-auth/src/TokenManagers/MinecraftBedrockTokenManager.js
-@@ -1,5 +1,5 @@
- const debug = require('debug')('prismarine-auth')
--
-+const fetch = require('electron-fetch').default;
- const { Endpoints } = require('../common/Constants')
- const { checkStatus } = require('../common/Util')
-
-diff --git a/node_modules/prismarine-auth/src/TokenManagers/MinecraftJavaTokenManager.js b/node_modules/prismarine-auth/src/TokenManagers/MinecraftJavaTokenManager.js
-index c89e8f1..79df714 100644
---- a/node_modules/prismarine-auth/src/TokenManagers/MinecraftJavaTokenManager.js
-+++ b/node_modules/prismarine-auth/src/TokenManagers/MinecraftJavaTokenManager.js
-@@ -1,6 +1,6 @@
- const debug = require('debug')('prismarine-auth')
- const crypto = require('crypto')
--
-+const fetch = require('electron-fetch').default;
- const { Endpoints, fetchOptions } = require('../common/Constants')
- const { checkStatus } = require('../common/Util')
-
-diff --git a/node_modules/prismarine-auth/src/TokenManagers/MsaTokenManager.js b/node_modules/prismarine-auth/src/TokenManagers/MsaTokenManager.js
-index 568385d..fb24dd6 100644
---- a/node_modules/prismarine-auth/src/TokenManagers/MsaTokenManager.js
-+++ b/node_modules/prismarine-auth/src/TokenManagers/MsaTokenManager.js
-@@ -1,6 +1,6 @@
- const msal = require('@azure/msal-node')
- const debug = require('debug')('prismarine-auth')
--
-+const fetch = require('electron-fetch').default;
- class MsaTokenManager {
- constructor (msalConfig, scopes, cache) {
- this.msaClientId = msalConfig.auth.clientId
-diff --git a/node_modules/prismarine-auth/src/TokenManagers/PlayfabTokenManager.js b/node_modules/prismarine-auth/src/TokenManagers/PlayfabTokenManager.js
-index 3ba041f..4eca82c 100644
---- a/node_modules/prismarine-auth/src/TokenManagers/PlayfabTokenManager.js
-+++ b/node_modules/prismarine-auth/src/TokenManagers/PlayfabTokenManager.js
-@@ -1,5 +1,5 @@
- const debug = require('debug')('prismarine-auth')
--
-+const fetch = require('electron-fetch').default;
- const { Endpoints } = require('../common/Constants')
-
- class PlayfabTokenManager {
-diff --git a/node_modules/prismarine-auth/src/TokenManagers/XboxTokenManager.js b/node_modules/prismarine-auth/src/TokenManagers/XboxTokenManager.js
-index dd79fd5..fc6e5ad 100644
---- a/node_modules/prismarine-auth/src/TokenManagers/XboxTokenManager.js
-+++ b/node_modules/prismarine-auth/src/TokenManagers/XboxTokenManager.js
-@@ -1,5 +1,5 @@
- const crypto = require('crypto')
--
-+const fetch = require('electron-fetch').default;
- const XboxLiveAuth = require('@xboxreplay/xboxlive-auth')
- const debug = require('debug')('prismarine-auth')
- const { SmartBuffer } = require('smart-buffer')
diff --git a/src/fetch-polyfill.js b/src/fetch-polyfill.js
new file mode 100644
index 0000000..a989da8
--- /dev/null
+++ b/src/fetch-polyfill.js
@@ -0,0 +1,7 @@
+'use strict'
+
+if (typeof globalThis.fetch !== 'function') {
+ const electronFetch = require('electron-fetch')
+
+ globalThis.fetch = electronFetch.default || electronFetch
+}
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index 3d13c68..8bc4706 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,24 +1,42 @@
-const { program } = require('commander');
+require('./fetch-polyfill')
+
+const { program } = require('commander')
program
.option('-a, --autostart', 'Automatically starts the program without the start window (all below options must be set)')
.option('-e, --platform ', 'Platform (accepted values: java, bedrock)')
.option('-v, --version ', 'The version to use (not needed for Bedrock)')
.option('-c, --connect ', 'The address of the server to connect to')
- .option('-p, --connect-port ', 'The port of the server to connect to')
- .option('-P, --listen-port ', 'The port to listen on')
+ .option('-p, --connect-port ', 'The port of the server to connect to')
+ .option('-P, --listen-port ', 'The port to listen on')
program.parse(process.argv)
+
const options = program.opts()
if (options.autostart) {
- if (!options.platform || !(options.version || options.platform !== 'java') || !options.connect || !options.connectPort || !options.listenPort) {
- console.log('Not all required options were passed.')
- program.help()
- }
+ if (
+ !options.platform ||
+ !(options.version || options.platform !== 'java') ||
+ !options.connect ||
+ !options.connectPort ||
+ !options.listenPort
+ ) {
+ console.log('Not all required options were passed.')
+ program.help()
+ }
}
-const {app, BrowserWindow, ipcMain, clipboard, Menu, dialog, shell } = require('electron')
+const {
+ app,
+ BrowserWindow,
+ ipcMain,
+ clipboard,
+ Menu,
+ dialog,
+ shell
+} = require('electron')
+
app.allowRendererProcessReuse = true
const fs = require('fs')
@@ -26,11 +44,13 @@ const Store = require('electron-store')
const store = new Store()
-let proxy // Defined later when an option is chosen
-const resourcesPath = fs.existsSync(process.resourcesPath.concat('/app/'))
- ? process.resourcesPath.concat('/app/') // Packaged with electron-forge
- : './' // npm start
+let proxy
+const resourcesPath = fs.existsSync(
+ process.resourcesPath.concat('/app/')
+)
+ ? process.resourcesPath.concat('/app/')
+ : './'
const javaProxy = require('./proxy/java/proxy.js')
const bedrockProxy = require('./proxy/bedrock/proxy.js')
@@ -42,339 +62,1476 @@ const windowStateKeeper = require('electron-window-state')
const unhandled = require('electron-unhandled')
const osDataFolder = app.getPath('appData')
-
const dataFolder = osDataFolder + '/pakkit'
-var currentScriptFile = null
+let currentScriptFile = null
-function makeMenu(direction, text, id, invalid, noData) {
- if (direction !== 'clientbound' && direction !== 'serverbound') {
- // This probably isn't a packet
- return
- }
+let activeLogSave = null
- const menuData = [
- {
- icon: resourcesPath + `icons/${direction + (invalid ? '-invalid' : '')}.png`,
- label: text,
- enabled: false
- },
- {
- type: 'separator'
- },
- {
- label: 'Edit and resend',
- click: () => {
- BrowserWindow.getAllWindows()[0].send('editAndResend', JSON.stringify({
- id: id
- }))
- },
- visible: proxy.capabilities.modifyPackets
- },
- {
- label: 'Hide all packets of this type',
- click: () => {
- BrowserWindow.getAllWindows()[0].send('hideAllOfType', JSON.stringify({
- // Packet ID from link URL
- id: id
- }))
- }
- }
- ]
+function sendToWindow (win, channel, ...args) {
+ if (
+ !win ||
+ (typeof win.isDestroyed === 'function' && win.isDestroyed()) ||
+ !win.webContents ||
+ (typeof win.webContents.isDestroyed === 'function' && win.webContents.isDestroyed())
+ ) {
+ return false
+ }
- if (!noData) {
- menuData.splice(2, 0,
- {
- label: proxy.capabilities.jsonData ? 'Copy JSON data' : 'Copy data',
- click: () => {
- BrowserWindow.getAllWindows()[0].send('copyPacketData', JSON.stringify({
- id: id
- }))
- }
- }
- )
+ try {
+ win.webContents.send(channel, ...args)
+ return true
+ } catch (error) {
+ if (!String(error && error.message).toLowerCase().includes('destroyed')) {
+ console.error(error)
}
+ return false
+ }
+}
- if (!noData && text.split(' ')[1] === 'position' && direction === 'clientbound') {
- menuData.splice(3, 0,
- {
- label: 'Copy teleport as command',
- click: () => {
- BrowserWindow.getAllWindows()[0].send('copyTeleportCommand', JSON.stringify({
- id: id
- }))
- }
- }
- )
- }
+function makeMenu (direction, text, id, invalid, noData) {
+ if (
+ direction !== 'clientbound' &&
+ direction !== 'serverbound'
+ ) {
+ return
+ }
- if (proxy.capabilities.rawData) {
- menuData.splice(3, 0,
- {
- label: 'Copy hex data',
- click: () => {
- BrowserWindow.getAllWindows()[0].send('copyHexData', JSON.stringify({
- id: id
- }))
- }
- }
+ const menuData = [
+ {
+ icon:
+ resourcesPath +
+ `icons/${direction + (invalid ? '-invalid' : '')}.png`,
+ label: text,
+ enabled: false
+ },
+ {
+ type: 'separator'
+ },
+ {
+ label: 'Edit and resend',
+ click: () => {
+ sendToWindow(BrowserWindow.getAllWindows()[0],
+ 'editAndResend',
+ JSON.stringify({
+ id
+ })
)
+ },
+ visible: proxy.capabilities.modifyPackets
+ },
+ {
+ label: 'Hide all packets of this type',
+ click: () => {
+ sendToWindow(BrowserWindow.getAllWindows()[0],
+ 'hideAllOfType',
+ JSON.stringify({
+ id
+ })
+ )
+ }
}
+ ]
+
+ if (!noData) {
+ menuData.splice(
+ 2,
+ 0,
+ {
+ label: proxy.capabilities.jsonData
+ ? 'Copy JSON data'
+ : 'Copy data',
+ click: () => {
+ sendToWindow(BrowserWindow.getAllWindows()[0],
+ 'copyPacketData',
+ JSON.stringify({
+ id
+ })
+ )
+ }
+ }
+ )
+ }
- return Menu.buildFromTemplate(menuData)
+ if (
+ !noData &&
+ text.split(' ')[1] === 'position' &&
+ direction === 'clientbound'
+ ) {
+ menuData.splice(
+ 3,
+ 0,
+ {
+ label: 'Copy teleport as command',
+ click: () => {
+ sendToWindow(BrowserWindow.getAllWindows()[0],
+ 'copyTeleportCommand',
+ JSON.stringify({
+ id
+ })
+ )
+ }
+ }
+ )
+ }
+
+ if (proxy.capabilities.rawData) {
+ menuData.splice(
+ 3,
+ 0,
+ {
+ label: 'Copy hex data',
+ click: () => {
+ sendToWindow(BrowserWindow.getAllWindows()[0],
+ 'copyHexData',
+ JSON.stringify({
+ id
+ })
+ )
+ }
+ }
+ )
+ }
+
+ return Menu.buildFromTemplate(menuData)
}
-function createWindow() {
- // Let us register listeners on the window, so we can update the state
- // automatically (the listeners will be removed when the window is closed)
- // and restore the maximized or full screen state
-
- // Create the browser window.
- const win = new BrowserWindow({
- height: store.get('authConsentGiven') ? 550 : 650,
- width: 480,
- // resizable: false,
- // frame: false,
- webPreferences: {
- nodeIntegration: true,
- contextIsolation: false,
- enableRemoteModule: true
- },
- icon: resourcesPath + 'icons/icon.png'
- })
+function createWindow () {
+ const win = new BrowserWindow({
+ height: store.get('authConsentGiven') ? 550 : 650,
+ width: 480,
+ webPreferences: {
+ nodeIntegration: true,
+ contextIsolation: false,
+ enableRemoteModule: true
+ },
+ icon: resourcesPath + 'icons/icon.png'
+ })
- win.setMenuBarVisibility(false)
+ win.setMenuBarVisibility(false)
- // Open the DevTools.
- // win.webContents.openDevTools()
- electronLocalShortcut.register(win, 'F12', () => {
- win.openDevTools()
- })
+ electronLocalShortcut.register(win, 'F12', () => {
+ win.openDevTools()
+ })
- win.webContents.setWindowOpenHandler(function(details) {
- shell.openExternal(details.url)
- return { action: 'deny' }
- })
+ win.webContents.setWindowOpenHandler(details => {
+ shell.openExternal(details.url)
- unhandled({
- logger: (err) => {
- win.send('error', JSON.stringify({msg: err.message, stack: err.stack}))
- console.log(err.stack)
- console.error(err)
- },
- showDialog: false
- })
+ return {
+ action: 'deny'
+ }
+ })
- win.setMenu(null)
- // and load the index.html of the app.
- if (options.autostart) {
- startProxy({
- // TODO: make online-mode working in headless via command-line parameters
- consent: false,
- onlineMode: false,
- connectAddress: options.connect,
- connectPort: options.connectPort,
- listenPort: options.listenPort,
- platform: options.platform,
- version: options.version
+ unhandled({
+ logger: err => {
+ sendToWindow(win,
+ 'error',
+ JSON.stringify({
+ msg: err.message,
+ stack: err.stack
})
- } else {
- win.loadFile('html/startPage/index.html')
- }
+ )
+
+ console.log(err.stack)
+ console.error(err)
+ },
+ showDialog: false
+ })
+
+ win.setMenu(null)
+
+ if (options.autostart) {
+ startProxy({
+ consent: false,
+ onlineMode: false,
+ connectAddress: options.connect,
+ connectPort: options.connectPort,
+ listenPort: options.listenPort,
+ platform: options.platform,
+ version: options.version
+ })
+ } else {
+ win.loadFile('html/startPage/index.html')
+ }
}
-// This method will be called when Electron has finished
-// initialization and is ready to create browser windows.
-// Some APIs can only be used after this event occurs.
app.whenReady().then(createWindow)
-// Quit when all windows are closed.
app.on('window-all-closed', () => {
- // On macOS it is common for applications and their menu bar
- // to stay active until the user quits explicitly with Cmd + Q
- if (process.platform !== 'darwin') {
- if (proxy) {
- proxy.end()
- }
- app.quit()
+ if (process.platform !== 'darwin') {
+ if (proxy) {
+ proxy.end()
+ }
+
+ if (activeLogSave) {
+ activeLogSave.stream.destroy()
+ activeLogSave = null
}
+
+ app.quit()
+ }
})
app.on('activate', () => {
- // On macOS it's common to re-create a window in the app when the
- // dock icon is clicked and there are no other windows open.
- if (BrowserWindow.getAllWindows().length === 0) {
- createWindow()
- }
+ if (BrowserWindow.getAllWindows().length === 0) {
+ createWindow()
+ }
})
ipcMain.on('startProxy', (event, arg) => {
- const ipcMessage = JSON.parse(arg)
- startProxy(ipcMessage)
+ const ipcMessage = JSON.parse(arg)
+ startProxy(ipcMessage)
})
function showAuthCode (data) {
- const win = BrowserWindow.getAllWindows()[0]
- win.send('showAuthCode', JSON.stringify(data))
+ const win = BrowserWindow.getAllWindows()[0]
+
+ sendToWindow(win,
+ 'showAuthCode',
+ JSON.stringify(data)
+ )
}
function startProxy (args) {
- switch(args.platform){
- case 'bedrock':
- proxy = bedrockProxy
- break;
- case 'java':
- proxy = javaProxy
- break;
- }
+ switch (args.platform) {
+ case 'bedrock':
+ proxy = bedrockProxy
+ break
+
+ case 'java':
+ proxy = javaProxy
+ break
+
+ default:
+ throw new Error(
+ `Unsupported platform: ${args.platform}`
+ )
+ }
- const win = BrowserWindow.getAllWindows()[0]
+ const win = BrowserWindow.getAllWindows()[0]
- packetHandler.init(BrowserWindow.getAllWindows()[0], ipcMain, proxy)
- proxy.startProxy(args.connectAddress, args.connectPort, args.listenPort, args.version, args.onlineMode,
- args.consent, packetHandler.packetHandler, packetHandler.messageHandler , dataFolder, () => {
- win.send('updateFiltering', '')
- }, showAuthCode)
+ packetHandler.init(
+ win,
+ ipcMain,
+ proxy
+ )
- win.loadFile('html/mainPage/index.html')
+ proxy.startProxy(
+ args.connectAddress,
+ args.connectPort,
+ args.listenPort,
+ args.version,
+ args.onlineMode,
+ args.consent,
+ packetHandler.packetHandler,
+ packetHandler.messageHandler,
+ dataFolder,
+ () => {
+ sendToWindow(win, 'updateFiltering', '')
+ },
+ showAuthCode
+ )
- // Load the previous state with fallback to defaults
- const mainWindowState = windowStateKeeper({
- defaultWidth: 1000,
- defaultHeight: 800
- });
+ win.loadFile('html/mainPage/index.html')
- win.setResizable(true)
- //win.setPosition(mainWindowState.x, mainWindowState.y) // TODO: figure out why this causes an issue
- win.setSize(mainWindowState.width, mainWindowState.height)
+ const mainWindowState = windowStateKeeper({
+ defaultWidth: 1000,
+ defaultHeight: 800
+ })
- mainWindowState.manage(win)
+ win.setResizable(true)
+
+ win.setSize(
+ mainWindowState.width,
+ mainWindowState.height
+ )
+
+ mainWindowState.manage(win)
}
-ipcMain.on('proxyCapabilities', (event, arg) => {
- event.returnValue = proxy.capabilities
+ipcMain.on('proxyCapabilities', event => {
+ event.returnValue = proxy.capabilities
})
ipcMain.on('copyToClipboard', (event, arg) => {
- clipboard.writeText(arg)
+ clipboard.writeText(arg === undefined || arg === null ? '' : String(arg))
})
ipcMain.on('contextMenu', (event, arg) => {
- const ipcMessage = JSON.parse(arg)
- makeMenu(ipcMessage.direction, ipcMessage.text, ipcMessage.id, ipcMessage.invalid, ipcMessage.noData).popup(BrowserWindow.getAllWindows()[0])
+ const ipcMessage = JSON.parse(arg)
+
+ makeMenu(
+ ipcMessage.direction,
+ ipcMessage.text,
+ ipcMessage.id,
+ ipcMessage.invalid,
+ ipcMessage.noData
+ ).popup(BrowserWindow.getAllWindows()[0])
})
-ipcMain.on('relaunchApp', (event, arg) => {
- app.relaunch()
- app.exit()
+ipcMain.on('relaunchApp', () => {
+ app.relaunch()
+ app.exit()
})
-ipcMain.on('saveLog', async (event, arg) => {
- const win = BrowserWindow.getAllWindows()[0]
+function waitForStreamOpen (stream) {
+ return new Promise((resolve, reject) => {
+ if (!stream.pending) {
+ resolve()
+ return
+ }
- const result = await dialog.showSaveDialog(win, {
- filters: [
- { name: 'pakkit log files', extensions: ['pakkit-json'] },
- // { name: 'All Files', extensions: ['*'] }
- ]
- })
+ const onOpen = () => {
+ cleanup()
+ resolve()
+ }
- if (!result.canceled) {
- const realPath = result.filePath.endsWith('.pakkit-json') ? result.filePath : result.filePath + '.pakkit-json'
- console.log('Saving log to', realPath)
- fs.writeFile(realPath, arg, function (err) {
- if (err) throw err;
- console.log('Saved!');
- })
+ const onError = err => {
+ cleanup()
+ reject(err)
}
-})
-ipcMain.on('loadLog', async (event, arg) => {
- const win = BrowserWindow.getAllWindows()[0]
+ const cleanup = () => {
+ stream.removeListener('open', onOpen)
+ stream.removeListener('error', onError)
+ }
- const result = await dialog.showOpenDialog(win, {
- filters: [
- { name: 'pakkit log files', extensions: ['pakkit-json'] },
- { name: 'All Files', extensions: ['*'] }
- ],
- properties: ['openFile']
- })
+ stream.once('open', onOpen)
+ stream.once('error', onError)
+ })
+}
- if (!result.canceled) {
- // It's an array, but we have multi-select off so it should only have one item
- console.log('Loading log from', result.filePaths[0])
- fs.readFile(result.filePaths[0], 'utf-8', function(err, data) {
- if (err) throw err;
- console.log('File has been read')
- win.send('loadLogData', data)
- })
+function writeToStream (stream, data) {
+ return new Promise((resolve, reject) => {
+ if (
+ !stream ||
+ stream.destroyed ||
+ stream.writableEnded ||
+ stream.closed
+ ) {
+ reject(
+ new Error('Log stream is already closed')
+ )
+ return
}
-})
-ipcMain.on('saveAsScript', async (event, arg) => {
- const win = BrowserWindow.getAllWindows()[0]
+ let settled = false
- const result = await dialog.showSaveDialog(win, {
- title: "Save user script",
- filters: [
- { name: 'javascript files', extensions: ['js'] }
- ]
- })
+ const cleanup = () => {
+ stream.removeListener('error', onError)
+ stream.removeListener('drain', onDrain)
+ }
- if (!result.canceled) {
- const realPath = result.filePath.endsWith('.js') ? result.filePath : result.filePath + '.js'
- win.send('disableBtnScriptSave')
- console.log('Saving script to', realPath)
- fs.writeFile(realPath, arg, function (err) {
- if (err) throw err;
- console.log('Saved!');
- currentScriptFile = realPath
- win.send('enableBtnScriptSave', currentScriptFile)
- })
+ const finishResolve = () => {
+ if (settled) {
+ return
+ }
+
+ settled = true
+ cleanup()
+ resolve()
}
-})
-ipcMain.on('saveScript', async (event, arg) => {
- const win = BrowserWindow.getAllWindows()[0]
- const validScriptPath = (currentScriptFile != null || fs.existsSync(currentScriptFile) )
+ const finishReject = err => {
+ if (settled) {
+ return
+ }
+
+ settled = true
+ cleanup()
+ reject(err)
+ }
- win.send('disableBtnScriptSave')
+ const onError = err => {
+ finishReject(err)
+ }
- if (validScriptPath) {
- console.log('Overwrite script to', currentScriptFile)
- fs.writeFile(currentScriptFile, arg, function (err) {
- if (err) throw err;
- console.log('Saved!');
- win.send('enableBtnScriptSave', currentScriptFile)
- })
+ const onDrain = () => {
+ finishResolve()
}
+
+ stream.once('error', onError)
+
+ let canContinue
+
+ try {
+ canContinue = stream.write(data)
+ } catch (err) {
+ finishReject(err)
+ return
+ }
+
+ if (canContinue) {
+ finishResolve()
+ } else {
+ stream.once('drain', onDrain)
+ }
+ })
+}
+
+function finishStream (stream) {
+ return new Promise((resolve, reject) => {
+ if (!stream || stream.destroyed) {
+ reject(
+ new Error('Cannot finish a destroyed stream')
+ )
+ return
+ }
+
+ let settled = false
+
+ const cleanup = () => {
+ stream.removeListener('finish', onFinish)
+ stream.removeListener('error', onError)
+ }
+
+ const onFinish = () => {
+ if (settled) {
+ return
+ }
+
+ settled = true
+ cleanup()
+ resolve()
+ }
+
+ const onError = err => {
+ if (settled) {
+ return
+ }
+
+ settled = true
+ cleanup()
+ reject(err)
+ }
+
+ stream.once('finish', onFinish)
+ stream.once('error', onError)
+
+ stream.end()
+ })
+}
+
+function resetActiveLogSave (sessionId) {
+ if (
+ activeLogSave &&
+ activeLogSave.sessionId === sessionId
+ ) {
+ activeLogSave = null
+ }
+}
+
+function getActiveSaveOrThrow (sessionId) {
+ if (!activeLogSave) {
+ throw new Error('No active log save')
+ }
+
+ if (!sessionId) {
+ throw new Error('Missing save session ID')
+ }
+
+ if (activeLogSave.sessionId !== sessionId) {
+ throw new Error(
+ 'Invalid or expired save session'
+ )
+ }
+
+ return activeLogSave
+}
+
+ipcMain.handle('startSaveLog', async () => {
+ const win = BrowserWindow.getAllWindows()[0]
+
+ if (activeLogSave) {
+ console.warn(
+ 'startSaveLog ignored because a save is already running:',
+ activeLogSave.sessionId
+ )
+
+ return {
+ canceled: false,
+ busy: true,
+ sessionId: activeLogSave.sessionId,
+ filePath: activeLogSave.filePath,
+ packetCount: activeLogSave.packetCount
+ }
+ }
+
+ const result = await dialog.showSaveDialog(win, {
+ title: 'Save packet log',
+ filters: [
+ {
+ name: 'pakkit log files',
+ extensions: ['pakkit-json']
+ }
+ ]
+ })
+
+ if (result.canceled || !result.filePath) {
+ return {
+ canceled: true,
+ busy: false
+ }
+ }
+
+ const filePath =
+ result.filePath.endsWith('.pakkit-json')
+ ? result.filePath
+ : result.filePath + '.pakkit-json'
+
+ const sessionId =
+ `${Date.now()}-${Math.random()
+ .toString(36)
+ .slice(2)}`
+
+ const stream = fs.createWriteStream(
+ filePath,
+ {
+ flags: 'w',
+ encoding: 'utf8',
+ highWaterMark: 256 * 1024
+ }
+ )
+
+ const saveState = {
+ sessionId,
+ filePath,
+ stream,
+ packetCount: 0,
+ firstPacket: true,
+ failed: null,
+ queue: Promise.resolve(),
+ finishing: false,
+ canceled: false
+ }
+
+ activeLogSave = saveState
+
+ stream.on('error', err => {
+ saveState.failed = err
+
+ console.error(
+ 'Log stream failed:',
+ err
+ )
+ })
+
+ try {
+ await waitForStreamOpen(stream)
+ await writeToStream(stream, '[')
+
+ console.log(
+ 'Saving log to',
+ filePath,
+ 'session:',
+ sessionId
+ )
+
+ return {
+ canceled: false,
+ busy: false,
+ sessionId,
+ filePath
+ }
+ } catch (err) {
+ saveState.failed = err
+ stream.destroy()
+ resetActiveLogSave(sessionId)
+
+ try {
+ await fs.promises.unlink(filePath)
+ } catch (unlinkError) {
+ if (unlinkError.code !== 'ENOENT') {
+ console.error(
+ 'Could not remove failed save file:',
+ unlinkError
+ )
+ }
+ }
+
+ throw err
+ }
})
-ipcMain.on('loadScript', async (event, arg) => {
- const win = BrowserWindow.getAllWindows()[0]
- const result = await dialog.showOpenDialog(win, {
- title: "Load user script",
- filters: [
- { name: 'javascript files', extensions: ['js'] }
- ],
- properties: ['openFile']
+ipcMain.handle(
+ 'appendSaveLogChunk',
+ async (event, request) => {
+ if (
+ !request ||
+ typeof request !== 'object'
+ ) {
+ throw new TypeError(
+ 'Invalid appendSaveLogChunk request'
+ )
+ }
+
+ const {
+ sessionId,
+ packets
+ } = request
+
+ const saveState =
+ getActiveSaveOrThrow(sessionId)
+
+ if (saveState.finishing) {
+ throw new Error(
+ 'Log save is already finishing'
+ )
+ }
+
+ if (saveState.canceled) {
+ throw new Error(
+ 'Log save was canceled'
+ )
+ }
+
+ if (!Array.isArray(packets)) {
+ throw new TypeError(
+ 'packets must be an array'
+ )
+ }
+
+ if (saveState.failed) {
+ throw saveState.failed
+ }
+
+ saveState.queue =
+ saveState.queue.then(async () => {
+ if (saveState.failed) {
+ throw saveState.failed
+ }
+
+ if (saveState.canceled) {
+ throw new Error(
+ 'Log save was canceled'
+ )
+ }
+
+ const serializedPackets = []
+
+ for (const packet of packets) {
+ const serialized = JSON.stringify(packet)
+
+ if (serialized === undefined) {
+ continue
+ }
+
+ serializedPackets.push(serialized)
+ saveState.packetCount++
+ }
+
+ if (serializedPackets.length > 0) {
+ let output = serializedPackets.join(',')
+
+ if (!saveState.firstPacket) {
+ output = ',' + output
+ }
+
+ saveState.firstPacket = false
+
+ await writeToStream(
+ saveState.stream,
+ output
+ )
+ }
+ })
+
+ await saveState.queue
+
+ return {
+ success: true,
+ packetCount: saveState.packetCount
+ }
+ }
+)
+
+ipcMain.handle(
+ 'finishSaveLog',
+ async (event, request) => {
+ if (
+ !request ||
+ typeof request !== 'object'
+ ) {
+ throw new TypeError(
+ 'Invalid finishSaveLog request'
+ )
+ }
+
+ const {
+ sessionId
+ } = request
+
+ const saveState =
+ getActiveSaveOrThrow(sessionId)
+
+ if (saveState.finishing) {
+ throw new Error(
+ 'Log save is already finishing'
+ )
+ }
+
+ saveState.finishing = true
+
+ try {
+ await saveState.queue
+
+ if (saveState.failed) {
+ throw saveState.failed
+ }
+
+ if (saveState.canceled) {
+ throw new Error(
+ 'Log save was canceled'
+ )
+ }
+
+ await writeToStream(
+ saveState.stream,
+ ']'
+ )
+
+ await finishStream(
+ saveState.stream
+ )
+
+ console.log(
+ `Saved ${saveState.packetCount} packets to ${saveState.filePath}`
+ )
+
+ return {
+ success: true,
+ filePath: saveState.filePath,
+ packetCount: saveState.packetCount
+ }
+ } catch (err) {
+ saveState.failed = err
+
+ if (
+ saveState.stream &&
+ !saveState.stream.destroyed
+ ) {
+ saveState.stream.destroy()
+ }
+
+ throw err
+ } finally {
+ resetActiveLogSave(sessionId)
+ }
+ }
+)
+
+ipcMain.handle(
+ 'cancelSaveLog',
+ async (event, request) => {
+ if (
+ !request ||
+ typeof request !== 'object' ||
+ !request.sessionId
+ ) {
+ console.warn(
+ 'Ignoring cancelSaveLog without sessionId'
+ )
+
+ return {
+ success: false,
+ ignored: true
+ }
+ }
+
+ const saveState = activeLogSave
+
+ if (!saveState) {
+ return {
+ success: true,
+ alreadyClosed: true
+ }
+ }
+
+ if (
+ saveState.sessionId !==
+ request.sessionId
+ ) {
+ console.warn(
+ 'Ignoring cancelSaveLog for another session'
+ )
+
+ return {
+ success: false,
+ ignored: true
+ }
+ }
+
+ saveState.canceled = true
+
+ resetActiveLogSave(
+ saveState.sessionId
+ )
+
+ if (
+ saveState.stream &&
+ !saveState.stream.destroyed
+ ) {
+ saveState.stream.destroy()
+ }
+
+ try {
+ await fs.promises.unlink(
+ saveState.filePath
+ )
+ } catch (err) {
+ if (err.code !== 'ENOENT') {
+ console.error(
+ 'Could not remove canceled log file:',
+ err
+ )
+ }
+ }
+
+ return {
+ success: true
+ }
+ }
+)
+
+let activeLogLoad = null
+
+function waitForRendererAck (webContents, channel, payload) {
+ return new Promise((resolve, reject) => {
+ const requestId =
+ `${Date.now()}-${Math.random().toString(36).slice(2)}`
+
+ const responseChannel =
+ `${channel}-ack-${requestId}`
+
+ const timeout = setTimeout(() => {
+ ipcMain.removeAllListeners(responseChannel)
+
+ reject(
+ new Error(
+ `Renderer did not acknowledge ${channel}`
+ )
+ )
+ }, 30000)
+
+ ipcMain.once(responseChannel, (event, response) => {
+ clearTimeout(timeout)
+
+ if (response && response.error) {
+ reject(
+ new Error(response.error)
+ )
+ return
+ }
+
+ resolve(response)
})
- if (!result.canceled) {
- win.send('disableBtnScriptSave')
-
- // It's an array, but we have multi-select off so it should only have one item
- console.log('Loading script from', result.filePaths[0])
- fs.readFile(result.filePaths[0], 'utf-8', function(err, data) {
- if (err) throw err;
- console.log('File has been read')
- currentScriptFile = result.filePaths[0]
- win.send('loadScriptData', data)
- win.send('enableBtnScriptSave', currentScriptFile)
- })
+ if (!sendToWindow({ webContents }, channel, {
+ requestId,
+ payload
+ })) {
+ clearTimeout(timeout)
+ ipcMain.removeAllListeners(responseChannel)
+ reject(new Error(`Renderer is unavailable for ${channel}`))
+ }
+ })
+}
+
+async function streamJsonArrayFile (
+ filePath,
+ onChunk,
+ options = {}
+) {
+ const readChunkSize =
+ options.readChunkSize || 1024 * 1024
+
+ const outputChunkSize =
+ options.outputChunkSize || 1000
+
+ const outputChunkBytes =
+ options.outputChunkBytes || 2 * 1024 * 1024
+
+ const stream = fs.createReadStream(filePath, {
+ encoding: 'utf8',
+ highWaterMark: readChunkSize
+ })
+
+ let buffer = ''
+ let scanIndex = 0
+
+ let arrayStarted = false
+ let arrayFinished = false
+
+ let objectStart = -1
+ let depth = 0
+ let inString = false
+ let escaped = false
+
+ let packetChunk = []
+ let packetChunkBytes = 0
+ let packetCount = 0
+
+ const flushPackets = async () => {
+ if (packetChunk.length === 0) {
+ return
+ }
+
+ const chunk = packetChunk
+ packetChunk = []
+ packetChunkBytes = 0
+
+ await onChunk(chunk, packetCount)
+ }
+
+ for await (const data of stream) {
+ buffer += data
+
+ while (scanIndex < buffer.length) {
+ const char = buffer[scanIndex]
+
+ if (!arrayStarted) {
+ if (/\s/.test(char)) {
+ scanIndex++
+ continue
+ }
+
+ if (char !== '[') {
+ throw new Error(
+ 'Invalid pakkit log: expected JSON array'
+ )
+ }
+
+ arrayStarted = true
+ scanIndex++
+ continue
+ }
+
+ if (arrayFinished) {
+ if (!/\s/.test(char)) {
+ throw new Error(
+ 'Invalid data after JSON array'
+ )
+ }
+
+ scanIndex++
+ continue
+ }
+
+ if (objectStart === -1) {
+ if (
+ /\s/.test(char) ||
+ char === ','
+ ) {
+ scanIndex++
+ continue
+ }
+
+ if (char === ']') {
+ arrayFinished = true
+ scanIndex++
+ continue
+ }
+
+ if (char !== '{') {
+ throw new Error(
+ `Invalid packet JSON near character ${scanIndex}`
+ )
+ }
+
+ objectStart = scanIndex
+ depth = 1
+ inString = false
+ escaped = false
+ scanIndex++
+ continue
+ }
+
+ if (inString) {
+ if (escaped) {
+ escaped = false
+ scanIndex++
+ continue
+ }
+
+ if (char === '\\') {
+ escaped = true
+ scanIndex++
+ continue
+ }
+
+ if (char === '"') {
+ inString = false
+ }
+
+ scanIndex++
+ continue
+ }
+
+ if (char === '"') {
+ inString = true
+ scanIndex++
+ continue
+ }
+
+ if (char === '{' || char === '[') {
+ depth++
+ scanIndex++
+ continue
+ }
+
+ if (char === '}' || char === ']') {
+ depth--
+
+ if (depth === 0) {
+ const jsonText = buffer.slice(
+ objectStart,
+ scanIndex + 1
+ )
+
+ let packet
+
+ try {
+ packet = JSON.parse(jsonText)
+ } catch (err) {
+ throw new Error(
+ `Invalid packet JSON near packet ${packetCount}: ${err.message}`
+ )
+ }
+
+ packetChunk.push(packet)
+ packetChunkBytes += Buffer.byteLength(jsonText, 'utf8')
+ packetCount++
+
+ scanIndex++
+ objectStart = -1
+
+ if (
+ packetChunk.length >= outputChunkSize ||
+ packetChunkBytes >= outputChunkBytes
+ ) {
+ await flushPackets()
+ }
+
+ buffer = buffer.slice(scanIndex)
+ scanIndex = 0
+
+ continue
+ }
+
+ scanIndex++
+ continue
+ }
+
+ scanIndex++
+ }
+
+ if (objectStart === -1 && scanIndex > 0) {
+ buffer = buffer.slice(scanIndex)
+ scanIndex = 0
+ }
+ }
+
+ if (!arrayStarted) {
+ throw new Error(
+ 'Invalid pakkit log: empty file'
+ )
+ }
+
+ if (objectStart !== -1) {
+ throw new Error(
+ 'Invalid pakkit log: incomplete packet object'
+ )
+ }
+
+ if (!arrayFinished) {
+ throw new Error(
+ 'Invalid pakkit log: missing closing bracket'
+ )
+ }
+
+ await flushPackets()
+
+ return packetCount
+}
+
+ipcMain.handle('startLoadLog', async () => {
+ const win = BrowserWindow.getAllWindows()[0]
+
+ if (activeLogLoad) {
+ return {
+ canceled: false,
+ busy: true
}
+ }
+
+ const result = await dialog.showOpenDialog(
+ win,
+ {
+ title: 'Load packet log',
+ filters: [
+ {
+ name: 'pakkit log files',
+ extensions: ['pakkit-json']
+ },
+ {
+ name: 'All Files',
+ extensions: ['*']
+ }
+ ],
+ properties: ['openFile']
+ }
+ )
+
+ if (
+ result.canceled ||
+ !result.filePaths ||
+ !result.filePaths[0]
+ ) {
+ return {
+ canceled: true,
+ busy: false
+ }
+ }
+
+ const filePath = result.filePaths[0]
+
+ const loadId =
+ `${Date.now()}-${Math.random().toString(36).slice(2)}`
+
+ activeLogLoad = {
+ loadId,
+ filePath,
+ canceled: false
+ }
+
+ const stat = await fs.promises.stat(filePath)
+
+ console.log(
+ 'Loading log from',
+ filePath
+ )
+
+ setImmediate(async () => {
+ const loadState = activeLogLoad
+
+ try {
+ await waitForRendererAck(
+ win.webContents,
+ 'loadLogStart',
+ {
+ loadId,
+ filePath,
+ fileSize: stat.size
+ }
+ )
+
+ const packetCount =
+ await streamJsonArrayFile(
+ filePath,
+ async packets => {
+ if (
+ !activeLogLoad ||
+ activeLogLoad.loadId !== loadId ||
+ loadState.canceled
+ ) {
+ throw new Error(
+ 'Log load canceled'
+ )
+ }
+
+ await waitForRendererAck(
+ win.webContents,
+ 'loadLogChunk',
+ {
+ loadId,
+ packets
+ }
+ )
+ },
+ {
+ readChunkSize: 1024 * 1024,
+ outputChunkSize: 1000,
+ outputChunkBytes: 2 * 1024 * 1024
+ }
+ )
+
+ await waitForRendererAck(
+ win.webContents,
+ 'loadLogFinish',
+ {
+ loadId,
+ packetCount
+ }
+ )
+
+ console.log(
+ `Loaded ${packetCount} packets from ${filePath}`
+ )
+ } catch (err) {
+ console.error(
+ 'Could not load log:',
+ err
+ )
+
+ sendToWindow(win,
+ 'loadLogError',
+ {
+ loadId,
+ error: err.message
+ }
+ )
+ } finally {
+ if (
+ activeLogLoad &&
+ activeLogLoad.loadId === loadId
+ ) {
+ activeLogLoad = null
+ }
+ }
+ })
+
+ return {
+ canceled: false,
+ busy: false,
+ loadId,
+ filePath,
+ fileSize: stat.size
+ }
})
-// In this file you can include the rest of your app's specific main process
-// code. You can also put them in separate files and require them here.
\ No newline at end of file
+ipcMain.handle('cancelLoadLog', async (event, request) => {
+ if (!activeLogLoad) {
+ return {
+ success: true
+ }
+ }
+
+ if (
+ !request ||
+ request.loadId !== activeLogLoad.loadId
+ ) {
+ return {
+ success: false,
+ ignored: true
+ }
+ }
+
+ activeLogLoad.canceled = true
+
+ return {
+ success: true
+ }
+})
+
+ipcMain.on(
+ 'saveAsScript',
+ async (event, arg) => {
+ const win =
+ BrowserWindow.getAllWindows()[0]
+
+ const result =
+ await dialog.showSaveDialog(
+ win,
+ {
+ title: 'Save user script',
+ filters: [
+ {
+ name: 'javascript files',
+ extensions: ['js']
+ }
+ ]
+ }
+ )
+
+ if (
+ result.canceled ||
+ !result.filePath
+ ) {
+ return
+ }
+
+ const realPath =
+ result.filePath.endsWith('.js')
+ ? result.filePath
+ : result.filePath + '.js'
+
+ sendToWindow(win,
+ 'disableBtnScriptSave'
+ )
+
+ console.log(
+ 'Saving script to',
+ realPath
+ )
+
+ fs.writeFile(
+ realPath,
+ arg,
+ err => {
+ if (err) {
+ console.error(
+ 'Could not save script:',
+ err
+ )
+
+ sendToWindow(win,
+ 'enableBtnScriptSave',
+ currentScriptFile
+ )
+
+ return
+ }
+
+ console.log('Saved!')
+
+ currentScriptFile = realPath
+
+ sendToWindow(win,
+ 'enableBtnScriptSave',
+ currentScriptFile
+ )
+ }
+ )
+ }
+)
+
+ipcMain.on(
+ 'saveScript',
+ async (event, arg) => {
+ const win =
+ BrowserWindow.getAllWindows()[0]
+
+ const validScriptPath =
+ currentScriptFile !== null &&
+ fs.existsSync(currentScriptFile)
+
+ sendToWindow(win,
+ 'disableBtnScriptSave'
+ )
+
+ if (!validScriptPath) {
+ console.error(
+ 'No valid script file selected'
+ )
+
+ sendToWindow(win,
+ 'enableBtnScriptSave',
+ currentScriptFile
+ )
+
+ return
+ }
+
+ console.log(
+ 'Overwrite script to',
+ currentScriptFile
+ )
+
+ fs.writeFile(
+ currentScriptFile,
+ arg,
+ err => {
+ if (err) {
+ console.error(
+ 'Could not overwrite script:',
+ err
+ )
+
+ sendToWindow(win,
+ 'enableBtnScriptSave',
+ currentScriptFile
+ )
+
+ return
+ }
+
+ console.log('Saved!')
+
+ sendToWindow(win,
+ 'enableBtnScriptSave',
+ currentScriptFile
+ )
+ }
+ )
+ }
+)
+
+ipcMain.on(
+ 'loadScript',
+ async () => {
+ const win =
+ BrowserWindow.getAllWindows()[0]
+
+ const result =
+ await dialog.showOpenDialog(
+ win,
+ {
+ title: 'Load user script',
+ filters: [
+ {
+ name: 'javascript files',
+ extensions: ['js']
+ }
+ ],
+ properties: ['openFile']
+ }
+ )
+
+ if (
+ result.canceled ||
+ !result.filePaths ||
+ !result.filePaths[0]
+ ) {
+ return
+ }
+
+ sendToWindow(win,
+ 'disableBtnScriptSave'
+ )
+
+ const filePath =
+ result.filePaths[0]
+
+ console.log(
+ 'Loading script from',
+ filePath
+ )
+
+ fs.readFile(
+ filePath,
+ 'utf8',
+ (err, data) => {
+ if (err) {
+ console.error(
+ 'Could not load script:',
+ err
+ )
+
+ sendToWindow(win,
+ 'enableBtnScriptSave',
+ currentScriptFile
+ )
+
+ return
+ }
+
+ console.log(
+ 'File has been read'
+ )
+
+ currentScriptFile = filePath
+
+ sendToWindow(win,
+ 'loadScriptData',
+ data
+ )
+
+ sendToWindow(win,
+ 'enableBtnScriptSave',
+ currentScriptFile
+ )
+ }
+ )
+ }
+)
diff --git a/src/packetHandler.js b/src/packetHandler.js
index 987e9af..9a83847 100644
--- a/src/packetHandler.js
+++ b/src/packetHandler.js
@@ -1,9 +1,9 @@
const _eval = require('node-eval')
BigInt.prototype.toJSON = function () {
- const int = Number.parseInt(this.toString());
- return int ?? this.toString();
-};
+ const value = Number(this)
+ return Number.isSafeInteger(value) ? value : this.toString()
+}
let mainWindow
let ipcMain
@@ -12,6 +12,27 @@ let scriptingEnabled = false
let currentScript
let currentScriptModule
+function sendToRenderer (channel, payload) {
+ if (
+ !mainWindow ||
+ (typeof mainWindow.isDestroyed === 'function' && mainWindow.isDestroyed()) ||
+ !mainWindow.webContents ||
+ (typeof mainWindow.webContents.isDestroyed === 'function' && mainWindow.webContents.isDestroyed())
+ ) {
+ return false
+ }
+
+ try {
+ mainWindow.send(channel, payload)
+ return true
+ } catch (error) {
+ if (!String(error && error.message).toLowerCase().includes('destroyed')) {
+ console.error(error)
+ }
+ return false
+ }
+}
+
const server = {
sendPacket: function (meta, data) {
proxy.writeToServer(meta, data, true)
@@ -29,6 +50,12 @@ exports.init = function (window, passedIpcMain, passedProxy) {
ipcMain = passedIpcMain
proxy = passedProxy
+ if (window && typeof window.once === 'function') {
+ window.once('closed', () => {
+ if (mainWindow === window) mainWindow = null
+ })
+ }
+
ipcMain.on('injectPacket', (event, arg) => {
const ipcMessage = JSON.parse(arg)
if (ipcMessage.direction === 'clientbound') {
@@ -52,7 +79,7 @@ exports.init = function (window, passedIpcMain, passedProxy) {
})
}
-exports.packetHandler = function (direction, meta, data, id, canUseScripting, packetValid) {
+exports.packetHandler = function (direction, meta, data, id, canUseScripting, packetValid, raw) {
try {
// TODO: Maybe write raw data?
if (proxy.capabilities.scriptingSupport && canUseScripting && scriptingEnabled) {
@@ -62,13 +89,15 @@ exports.packetHandler = function (direction, meta, data, id, canUseScripting, pa
currentScriptModule.upstreamHandler(meta, data, server, client)
}
}
- let raw = proxy.getRaw(meta.name, data)
- mainWindow.send('packet', JSON.stringify({ meta: meta, data: data, direction: direction, hexIdString: id, raw: raw, time: Date.now(), packetValid: packetValid }))
+ const packetRaw = raw || proxy.getRaw(direction, meta.name, data)
+ sendToRenderer('packet', JSON.stringify({ meta: meta, data: data, direction: direction, hexIdString: id, raw: packetRaw, time: Date.now(), packetValid: packetValid }))
} catch (err) {
- console.error(err)
+ if (!String(err && err.message).toLowerCase().includes('destroyed')) {
+ console.error(err)
+ }
}
}
exports.messageHandler = function (header, info, fatal) {
- mainWindow.send('message', JSON.stringify({ header: header, info: info, fatal: fatal }))
+ sendToRenderer('message', JSON.stringify({ header: header, info: info, fatal: fatal }))
}
diff --git a/src/proxy/java/proxy.js b/src/proxy/java/proxy.js
index 8479394..087761f 100644
--- a/src/proxy/java/proxy.js
+++ b/src/proxy/java/proxy.js
@@ -1,34 +1,112 @@
-// Modified from https://github.com/PrismarineJS/node-minecraft-protocol/blob/master/examples/proxy/proxy.js
+// Modified from:
+// https://github.com/PrismarineJS/node-minecraft-protocol/blob/master/examples/proxy/proxy.js
const mc = require('minecraft-protocol')
const minecraftFolder = require('minecraft-folder-path')
-const {getRaw} = require("../bedrock/proxy");
const states = mc.states
+let proxyServer
let realClient
let realServer
-let toClientMappings
-let toServerMappings
+let toClientMappings = {}
+let toServerMappings = {}
let storedCallback
let scriptingEnabled = false
+let authWindowOpen = false
-// https://gist.github.com/timoxley/1689041
-function isPortTaken (port, fn) {
+function isPortTaken (port, callback) {
const net = require('net')
+
const tester = net.createServer()
- .once('error', function (err) {
- if (err.code != 'EADDRINUSE') return fn(err)
- fn(null, true)
+ .once('error', function (error) {
+ if (error.code !== 'EADDRINUSE') {
+ callback(error)
+ return
+ }
+
+ callback(null, true)
})
- .once('listening', function() {
- tester.once('close', function() { fn(null, false) })
+ .once('listening', function () {
+ tester
+ .once('close', function () {
+ callback(null, false)
+ })
.close()
})
.listen(port)
}
+function isPlayState (state) {
+ return state === states.PLAY || state === 'play'
+}
+
+function isConfigurationState (state) {
+ return state === states.CONFIGURATION || state === 'configuration'
+}
+
+function getMappingId (mappings, packetName) {
+ if (!mappings || typeof packetName !== 'string') {
+ return undefined
+ }
+
+ return Object.keys(mappings)
+ .find(id => mappings[id] === packetName)
+}
+
+function safeCall (callback, ...args) {
+ if (typeof callback === 'function') {
+ try {
+ callback(...args)
+ } catch (error) {
+ if (!String(error && error.message).toLowerCase().includes('destroyed')) {
+ console.error(error)
+ }
+ }
+ }
+}
+
+function createCustomPackets (mcdata) {
+ const majorVersion = mcdata.version && mcdata.version.majorVersion
+ if (!majorVersion) return undefined
+
+ const particleType = mcdata.protocol &&
+ mcdata.protocol.play &&
+ mcdata.protocol.play.toClient &&
+ mcdata.protocol.play.toClient.types &&
+ mcdata.protocol.play.toClient.types.packet_world_particles
+
+ if (!Array.isArray(particleType) || !Array.isArray(particleType[1])) {
+ return undefined
+ }
+
+ const usesParticleRegistry = particleType[1]
+ .some(field => field && field.type === 'Particle')
+
+ if (!usesParticleRegistry) return undefined
+
+ const fields = particleType[1].map((field, index) => ({
+ name: index === 0 ? 'raw' : (field.name || `field${index}`),
+ type: 'restBuffer'
+ }))
+
+ return {
+ [majorVersion]: {
+ play: {
+ toClient: {
+ types: {
+ packet_world_particles: [
+ 'container',
+ fields
+ ]
+ }
+ }
+ }
+ }
+ }
+}
+
exports.capabilities = {
modifyPackets: true,
jsonData: true,
@@ -36,269 +114,802 @@ exports.capabilities = {
scriptingSupport: true,
clientboundPackets: [],
serverboundPackets: [],
- // TODO: Only for latest, or fetch older pages
wikiVgPage: 'https://wiki.vg/Protocol',
versionId: undefined
}
-let authWindowOpen = false
-
-exports.startProxy = function (host, port, listenPort, version, onlineMode, authConsent, callback, messageCallback, dataFolder,
- updateFilteringCallback, authCodeCallback) {
+exports.startProxy = function (
+ host,
+ port,
+ listenPort,
+ version,
+ onlineMode,
+ authConsent,
+ callback,
+ messageCallback,
+ dataFolder,
+ updateFilteringCallback,
+ authCodeCallback
+) {
storedCallback = callback
- authConsent = false
- // . cannot be in a JSON property name with electron-store
- exports.capabilities.versionId = 'java-node-minecraft-protocol-' + version.split('.').join('-')
+ port = Number(port)
+ listenPort = Number(listenPort)
+
+ exports.capabilities.versionId =
+ 'java-node-minecraft-protocol-' +
+ version.split('.').join('-')
+
+ const mcdata = require('minecraft-data')(version)
+
+ if (!mcdata) {
+ safeCall(
+ messageCallback,
+ 'Unable to start pakkit',
+ `Minecraft data is unavailable for version ${version}`,
+ true
+ )
+ return
+ }
+
+ const playProtocol = mcdata.protocol &&
+ mcdata.protocol.play
+
+ if (!playProtocol) {
+ safeCall(
+ messageCallback,
+ 'Unable to start pakkit',
+ `Play protocol data is unavailable for version ${version}`,
+ true
+ )
+ return
+ }
+
+ const clientPacketType =
+ playProtocol.toClient &&
+ playProtocol.toClient.types &&
+ playProtocol.toClient.types.packet &&
+ playProtocol.toClient.types.packet[1] &&
+ playProtocol.toClient.types.packet[1][0] &&
+ playProtocol.toClient.types.packet[1][0].type &&
+ playProtocol.toClient.types.packet[1][0].type[1]
+
+ const serverPacketType =
+ playProtocol.toServer &&
+ playProtocol.toServer.types &&
+ playProtocol.toServer.types.packet &&
+ playProtocol.toServer.types.packet[1] &&
+ playProtocol.toServer.types.packet[1][0] &&
+ playProtocol.toServer.types.packet[1][0].type &&
+ playProtocol.toServer.types.packet[1][0].type[1]
+
+ toClientMappings =
+ (clientPacketType && clientPacketType.mappings) || {}
- const mcdata = require('minecraft-data')(version) // Used to get packets, may remove if I find a better way
- toClientMappings = mcdata.protocol.play.toClient.types.packet[1][0].type[1].mappings
- toServerMappings = mcdata.protocol.play.toServer.types.packet[1][0].type[1].mappings
+ toServerMappings =
+ (serverPacketType && serverPacketType.mappings) || {}
- exports.capabilities.clientboundPackets = mcdata.protocol.play.toClient.types.packet[1][0].type[1].mappings
- exports.capabilities.serverboundPackets = mcdata.protocol.play.toServer.types.packet[1][0].type[1].mappings
+ exports.capabilities.clientboundPackets = toClientMappings
+ exports.capabilities.serverboundPackets = toServerMappings
- if (host.indexOf(':') !== -1) {
- port = host.substring(host.indexOf(':') + 1)
- host = host.substring(0, host.indexOf(':'))
+ const customPackets = createCustomPackets(mcdata)
+
+ const separatorIndex = host.lastIndexOf(':')
+
+ if (
+ separatorIndex !== -1 &&
+ host.indexOf(':') === separatorIndex
+ ) {
+ const hostPort = Number(host.substring(separatorIndex + 1))
+
+ if (Number.isFinite(hostPort)) {
+ port = hostPort
+ host = host.substring(0, separatorIndex)
+ }
}
- isPortTaken(listenPort, (err, taken) => {
- // TODO: Handle errors
- console.log(err, taken)
+
+ isPortTaken(listenPort, function (error, taken) {
+ if (error) {
+ safeCall(
+ messageCallback,
+ 'Unable to start pakkit',
+ error.message,
+ true
+ )
+ return
+ }
+
if (taken) {
- console.log('call')
- // Wait for the renderer to be ready
- setTimeout(() => {
- messageCallback('Unable to start pakkit', 'The port ' + listenPort + ' is in use. ' +
- 'Make sure to close any other instances of pakkit running on the same port or try a different port.', true)
- }, 1000)
- } else {
- let srv
- try {
- srv = mc.createServer({
- 'online-mode': false,
- port: listenPort,
- keepAlive: false,
- version: version
- })
- console.log('Proxy started (Java)!')
- } catch (err) {
- let header = 'Unable to start pakkit'
- let message = err.message
- if (err.message.includes('EADDRINUSE')) {
- message = 'The port ' + listenPort + ' is in use. ' +
- 'Make sure to close any other instances of pakkit running on the same port or try a different port.'
- }
- messageCallback(header, message)
- return
+ safeCall(
+ messageCallback,
+ 'Unable to start pakkit',
+ `The port ${listenPort} is in use. ` +
+ 'Close other Pakkit instances or choose another port.',
+ true
+ )
+ return
+ }
+
+ try {
+ proxyServer = mc.createServer({
+ 'online-mode': false,
+ port: listenPort,
+ keepAlive: false,
+ disableDefaultConfiguration: true,
+ customPackets,
+ hideErrors: true,
+ version
+ })
+ } catch (error) {
+ safeCall(
+ messageCallback,
+ 'Unable to start pakkit',
+ error.message,
+ true
+ )
+ return
+ }
+
+ console.log('Proxy started (Java)!')
+
+ proxyServer.on('login', function (client) {
+ realClient = client
+
+ const remoteAddress =
+ client.socket && client.socket.remoteAddress
+
+ console.log(
+ 'Incoming connection',
+ `(${remoteAddress || 'unknown'})`
+ )
+
+ let endedClient = false
+ let endedTargetClient = false
+
+ const pendingClientbound = []
+ const pendingServerbound = []
+
+ const MAX_PENDING_CLIENTBOUND = 4096
+ const MAX_PENDING_SERVERBOUND = 512
+
+ const forwardingErrors = new Set()
+
+ const clientOptions = {
+ host,
+ port,
+ username: client.username,
+ keepAlive: false,
+ version,
+ profilesFolder: authConsent
+ ? minecraftFolder
+ : dataFolder,
+ auth: onlineMode
+ ? 'microsoft'
+ : 'offline',
+ customPackets,
+ hideErrors: true,
+
+ onMsaCode: function (data) {
+ authWindowOpen = true
+
+ safeCall(authCodeCallback, data)
+ }
}
- srv.on('login', function (client) {
- realClient = client
- const addr = client.socket.remoteAddress
- console.log('Incoming connection', '(' + addr + ')')
- let endedClient = false
- let endedTargetClient = false
- client.on('end', function () {
- endedClient = true
- console.log('Connection closed by client', '(' + addr + ')')
- if (!endedTargetClient) { targetClient.end('End') }
- })
- client.on('error', function (err) {
- endedClient = true
- console.log('Connection error by client', '(' + addr + ')')
- console.log(err.stack)
- if (!endedTargetClient) { targetClient.end('Error') }
- })
- // if (authConsent) {
- // console.log('Will attempt to use launcher_profiles.json for online mode login data')
- // } else {
- // console.warn('Consent not given to use launcher_profiles.json - automatic online mode will not work')
- // }
- const clientOptions = {
- host: host,
- port: port,
- username: client.username,
- keepAlive: false,
- version: version,
- profilesFolder: authConsent ? minecraftFolder : dataFolder,
- auth: onlineMode ? 'microsoft' : 'offline',
- onMsaCode: function (data) {
- console.log('MSA code:', data.user_code)
- authWindowOpen = true
- authCodeCallback(data)
- }
+
+ const targetClient = mc.createClient(clientOptions)
+
+ realServer = targetClient
+
+ client.on('disconnect', reason => {
+ console.error('Client disconnect packet:', reason)
+ })
+ client.on('kick_disconnect', reason => {
+ console.error('Client kick disconnect:', reason)
+ })
+ targetClient.on('kick_disconnect', reason => {
+ console.error('Server disconnect packet:', reason)
+ })
+ targetClient.on('disconnect', reason => {
+ console.error('Server disconnect:', reason)
+ })
+
+ function bothSidesReady () {
+ return !endedClient &&
+ !endedTargetClient &&
+ isPlayState(client.state) &&
+ isPlayState(targetClient.state)
+ }
+
+ function enqueuePacket (
+ queue,
+ maxSize,
+ data,
+ meta,
+ raw
+ ) {
+ if (queue.length >= maxSize) {
+ queue.shift()
}
- let targetClient = mc.createClient(clientOptions)
- targetClient.on('session', function (session) {
- // Login complete - the dialog can be closed
- console.log('Login done')
- authWindowOpen = false
- authCodeCallback('close')
+
+ queue.push({
+ data,
+ meta: Object.assign({}, meta),
+ raw
})
+ }
+
+ function reportForwardingError (
+ direction,
+ meta,
+ error
+ ) {
+ const packetName =
+ meta && meta.name !== undefined
+ ? meta.name
+ : 'unknown'
+
+ const state =
+ meta && meta.state !== undefined
+ ? meta.state
+ : 'unknown'
+
+ const key =
+ `${direction}:${state}:${packetName}`
+
+ if (forwardingErrors.has(key)) {
+ return
+ }
- realServer = targetClient
+ forwardingErrors.add(key)
- function getId (meta, mappings) {
- let id
- if (typeof meta.name === 'number') {
- // Unknown packet ID
- id = '0x' + meta.name.toString(16).padStart(2, '0')
- meta.name = 'unknown'
+ console.error(
+ `Failed to forward ${key}: ${error.message}`
+ )
+ }
+
+ function forwardPacket (
+ destination,
+ meta,
+ data,
+ direction,
+ raw
+ ) {
+ if (!destination || !meta) {
+ return false
+ }
+
+ if (typeof meta.name !== 'string') {
+ return false
+ }
+
+ try {
+ if (raw) {
+ destination.writeRaw(raw)
} else {
- id = Object.keys(mappings).find(key => mappings[key] === meta.name)
+ destination.write(meta.name, data)
}
- return id
- }
-
- function handleServerboundPacket (data, meta, raw, packetValid) {
- // console.log('serverbound packet', meta, data)
- if (targetClient.state === states.PLAY && meta.state === states.PLAY) {
- const id = getId(meta, toServerMappings)
-
- // Stops standardjs from complaining (no-callback-literal)
- const direction = 'serverbound'
- const canUseScripting = true
-
- // callback(direction, meta, data, id)
- if (!endedTargetClient) {
- // When scripting is enabled, the script sends packets
- if (!scriptingEnabled) {
- // targetClient.write(meta.name, data)
- targetClient.writeRaw(raw)
- }
- callback(direction, meta, data, id, canUseScripting, packetValid)
- }
+ return true
+ } catch (error) {
+ reportForwardingError(
+ direction,
+ meta,
+ error
+ )
+
+ return false
+ }
+ }
+
+ function notifyPacket (
+ direction,
+ meta,
+ data,
+ mappings,
+ raw
+ ) {
+ const id = typeof meta.name === 'number'
+ ? `0x${meta.name.toString(16).padStart(2, '0')}`
+ : getMappingId(mappings, meta.name)
+
+ safeCall(
+ callback,
+ direction,
+ meta,
+ data,
+ id,
+ true,
+ true,
+ raw
+ )
+ }
+
+ function handleServerboundPacket (
+ data,
+ meta,
+ fromQueue,
+ raw
+ ) {
+ if (
+ !meta ||
+ (!isPlayState(meta.state) &&
+ !isConfigurationState(meta.state))
+ ) {
+ return
+ }
+
+ if (endedTargetClient) {
+ return
+ }
+
+ if (isPlayState(meta.state) && !bothSidesReady()) {
+ if (!fromQueue) {
+ enqueuePacket(
+ pendingServerbound,
+ MAX_PENDING_SERVERBOUND,
+ data,
+ meta,
+ raw
+ )
}
+
+ return
}
- function handleClientboundPacket (data, meta, raw, packetValid) {
- if (meta.state === states.PLAY && client.state === states.PLAY) {
- const id = getId(meta, toClientMappings)
-
- // Stops standardjs from complaining (no-callback-literal)
- const direction = 'clientbound'
- const canUseScripting = true
-
- // callback(direction, meta, data, id)
- if (!endedClient) {
- // When scripting is enabled, the script sends packets
- if (!scriptingEnabled) {
- // client.write(meta.name, data)
- client.writeRaw(raw)
- }
- callback(direction, meta, data, id, canUseScripting, packetValid)
- if (meta.name === 'set_compression') {
- client.compressionThreshold = data.threshold
- } // Set compression
+
+ if (isConfigurationState(meta.state)) {
+ const handledByTargetClient = new Set([
+ 'select_known_packs',
+ 'finish_configuration',
+ 'accept_code_of_conduct'
+ ])
+
+ if (handledByTargetClient.has(meta.name)) {
+ return
+ }
+
+ if (!isConfigurationState(targetClient.state)) {
+ if (!fromQueue) {
+ enqueuePacket(
+ pendingServerbound,
+ MAX_PENDING_SERVERBOUND,
+ data,
+ meta,
+ raw
+ )
}
+ return
}
}
- const bufferEqual = require('buffer-equal')
- targetClient.on('packet', function (data, meta, buffer, fullBuffer) {
- if (client.state !== states.PLAY || meta.state !== states.PLAY) { return }
- let packetValid = false
- try {
- const packetBuff = this.getRaw({ name: meta.name, params: data })
- if (!bufferEqual(fullBuffer, packetBuff)) {
- console.log('client<-server: Error in packet ' + meta.state + '.' + meta.name)
- console.log('received buffer', fullBuffer.toString('hex'))
- console.log('produced buffer', packetBuff.toString('hex'))
- console.log('received length', fullBuffer.length)
- console.log('produced length', packetBuff.length)
- } else {
- packetValid = true
- }
- } catch (e) {
- // TODO: handle?
+ if (!scriptingEnabled) {
+ const forwarded = forwardPacket(
+ targetClient,
+ meta,
+ data,
+ 'serverbound',
+ isPlayState(meta.state) ? raw : undefined
+ )
+
+ if (!forwarded) {
+ return
}
- handleClientboundPacket(data, meta, fullBuffer, packetValid)
- /* if (client.state === states.PLAY && brokenPackets.indexOf(packetId.value) !=== -1)
- {
- console.log(`client<-server: raw packet);
- console.log(packetData);
- if (!endedClient)
- client.writeRaw(buffer);
- } */
- })
- client.on('packet', function (data, meta, buffer, fullBuffer) {
- if (meta.state !== states.PLAY || targetClient.state !== states.PLAY) { return }
- const packetData = client.deserializer.parsePacketBuffer(fullBuffer).data.params
- let packetValid = false
- try {
- const packetBuff = this.getRaw({ name: meta.name, params: packetData })
- if (!bufferEqual(fullBuffer, packetBuff)) {
- console.log('client->server: Error in packet ' + meta.state + '.' + meta.name)
- console.log('received buffer', fullBuffer.toString('hex'))
- console.log('produced buffer', packetBuff.toString('hex'))
- console.log('received length', fullBuffer.length)
- console.log('produced length', packetBuff.length)
- } else {
- packetValid = true
- }
- } catch (e) {
- // TODO: handle?
+ }
+
+ if (isPlayState(meta.state)) {
+ notifyPacket('serverbound', meta, data, toServerMappings, raw)
+ }
+ }
+
+ function handleClientboundPacket (
+ data,
+ meta,
+ fromQueue,
+ raw
+ ) {
+ if (
+ !meta ||
+ (!isConfigurationState(meta.state) &&
+ !isPlayState(meta.state))
+ ) {
+ return
+ }
+
+ if (endedClient) {
+ return
+ }
+
+ if (isPlayState(meta.state) && !bothSidesReady()) {
+ if (!fromQueue) {
+ enqueuePacket(
+ pendingClientbound,
+ MAX_PENDING_CLIENTBOUND,
+ data,
+ meta,
+ raw
+ )
}
- if (typeof meta.name === 'number') {
- // Unknown packet ID so packet is invalid
- packetValid = false
+
+ return
+ }
+
+ if (!scriptingEnabled) {
+ const forwarded = forwardPacket(
+ client,
+ meta,
+ data,
+ 'clientbound',
+ raw
+ )
+
+ if (!forwarded) {
+ return
}
- handleServerboundPacket(packetData, meta, fullBuffer, packetValid)
- })
- targetClient.on('end', function () {
- endedTargetClient = true
- console.log('Connection closed by server', '(' + host + ':' + port + ')')
- if (!endedClient) { client.end('Connection closed by server ' + '(' + host + ':' + port + ')') }
- })
- targetClient.on('error', function (err) {
- endedTargetClient = true
- console.log('Connection error by server', '(' + host + ':' + port + ') ', err)
- console.log(err.stack)
- if (authWindowOpen) return
- let header = 'Unable to connect to server'
- let message = err.message
- if (err.message.includes('ECONNREFUSED')) {
- message = 'Unable to connect to the Java server at ' +
- host + ':' + port +
- '. Make sure the server is online.'
+ }
+
+ if (isPlayState(meta.state)) {
+ notifyPacket('clientbound', meta, data, toClientMappings, raw)
+ }
+ }
+
+ function flushPendingPackets () {
+ while (bothSidesReady() && pendingClientbound.length > 0) {
+ const packet = pendingClientbound.shift()
+
+ handleClientboundPacket(
+ packet.data,
+ packet.meta,
+ true,
+ packet.raw
+ )
+ }
+
+ const pendingCount = pendingServerbound.length
+ for (let i = 0; i < pendingCount; i++) {
+ const packet = pendingServerbound.shift()
+
+ const ready = isPlayState(packet.meta.state)
+ ? bothSidesReady()
+ : isConfigurationState(targetClient.state)
+
+ if (!ready) {
+ pendingServerbound.push(packet)
+ continue
}
- messageCallback(header, message)
- if (!endedClient) { client.end('pakkit - ' + header + '\n' + message) }
- })
+
+ handleServerboundPacket(
+ packet.data,
+ packet.meta,
+ true,
+ packet.raw
+ )
+ }
+ }
+
+ const readinessTimer = setInterval(function () {
+ if (endedClient || endedTargetClient) {
+ clearInterval(readinessTimer)
+ return
+ }
+
+ flushPendingPackets()
+ }, 10)
+
+ if (
+ typeof readinessTimer.unref === 'function'
+ ) {
+ readinessTimer.unref()
+ }
+
+ client.on('packet', function (data, meta, buffer, fullBuffer) {
+ handleServerboundPacket(
+ data,
+ meta,
+ false,
+ fullBuffer || buffer
+ )
})
- }
+
+ targetClient.on(
+ 'packet',
+ function (data, meta, buffer, fullBuffer) {
+ handleClientboundPacket(
+ data,
+ meta,
+ false,
+ fullBuffer || buffer
+ )
+ }
+ )
+
+ targetClient.on('session', function () {
+ authWindowOpen = false
+
+ safeCall(authCodeCallback, 'close')
+ })
+
+ client.on('end', function () {
+ if (endedClient) {
+ return
+ }
+
+ endedClient = true
+ clearInterval(readinessTimer)
+
+ pendingClientbound.length = 0
+ pendingServerbound.length = 0
+
+ console.log(
+ 'Connection closed by client',
+ `(${remoteAddress || 'unknown'})`
+ )
+
+ if (!endedTargetClient) {
+ try {
+ targetClient.end('End')
+ } catch (error) {}
+ }
+ })
+
+ client.on('error', function (error) {
+ if (endedClient) {
+ return
+ }
+
+ endedClient = true
+ clearInterval(readinessTimer)
+
+ pendingClientbound.length = 0
+ pendingServerbound.length = 0
+
+ console.error(
+ 'Connection error by client:',
+ error.message
+ )
+
+ if (!endedTargetClient) {
+ try {
+ targetClient.end('Error')
+ } catch (endError) {}
+ }
+ })
+
+ targetClient.on('end', function () {
+ if (endedTargetClient) {
+ return
+ }
+
+ endedTargetClient = true
+ clearInterval(readinessTimer)
+
+ pendingClientbound.length = 0
+ pendingServerbound.length = 0
+
+ console.log(
+ 'Connection closed by server',
+ `(${host}:${port})`
+ )
+
+ if (!endedClient) {
+ try {
+ client.end(
+ `Connection closed by server (${host}:${port})`
+ )
+ } catch (error) {}
+ }
+ })
+
+ targetClient.on('error', function (error) {
+ if (endedTargetClient) {
+ return
+ }
+
+ endedTargetClient = true
+ clearInterval(readinessTimer)
+
+ pendingClientbound.length = 0
+ pendingServerbound.length = 0
+
+ console.error(
+ `Connection error by server (${host}:${port}):`,
+ error.message
+ )
+
+ if (authWindowOpen) {
+ return
+ }
+
+ let message = error.message
+
+ if (
+ error.message &&
+ error.message.includes('ECONNREFUSED')
+ ) {
+ message =
+ `Unable to connect to ${host}:${port}. ` +
+ 'Make sure the server is online.'
+ }
+
+ safeCall(
+ messageCallback,
+ 'Unable to connect to server',
+ message,
+ true
+ )
+
+ if (!endedClient) {
+ try {
+ client.end(
+ 'pakkit - Unable to connect to server\n' +
+ message
+ )
+ } catch (endError) {}
+ }
+ })
+ })
+
+ proxyServer.on('error', function (error) {
+ safeCall(
+ messageCallback,
+ 'Pakkit proxy error',
+ error.message,
+ true
+ )
+ })
})
}
-exports.end = function () {}
-
-exports.getRaw = function (name, params) {
+exports.end = function () {
if (realClient) {
- return [...realClient.serializer.createPacketBuffer({ name, params })]
+ try {
+ realClient.end('Proxy stopped')
+ } catch (error) {}
+
+ realClient = undefined
+ }
+
+ if (realServer) {
+ try {
+ realServer.end('Proxy stopped')
+ } catch (error) {}
+
+ realServer = undefined
+ }
+
+ if (proxyServer) {
+ try {
+ if (
+ typeof proxyServer.close === 'function'
+ ) {
+ proxyServer.close()
+ } else if (
+ proxyServer.socketServer &&
+ typeof proxyServer.socketServer.close === 'function'
+ ) {
+ proxyServer.socketServer.close()
+ }
+ } catch (error) {}
+
+ proxyServer = undefined
}
}
-exports.writeToClient = function (meta, data, noCallback) {
+exports.getRaw = function (
+ direction,
+ name,
+ params
+) {
+ const connection =
+ direction === 'serverbound'
+ ? realServer
+ : realClient
+
+ if (
+ !connection ||
+ !connection.serializer
+ ) {
+ return undefined
+ }
+
+ try {
+ return connection.serializer
+ .createPacketBuffer({
+ name,
+ params
+ })
+ } catch (error) {
+ return undefined
+ }
+}
+
+exports.writeToClient = function (
+ meta,
+ data,
+ noCallback
+) {
+ if (!realClient) {
+ return false
+ }
+
if (typeof meta === 'string') {
- meta = { name: meta }
+ meta = {
+ name: meta,
+ state: states.PLAY
+ }
}
- realClient.write(meta.name, data)
- const id = Object.keys(toClientMappings).find(key => toClientMappings[key] === meta.name)
- if (!noCallback) {
- storedCallback('clientbound', meta, data, id) // TODO: indicator for injected packets
+
+ try {
+ realClient.write(meta.name, data)
+ } catch (error) {
+ return false
+ }
+
+ const id = getMappingId(
+ toClientMappings,
+ meta.name
+ )
+
+ if (
+ !noCallback &&
+ typeof storedCallback === 'function'
+ ) {
+ storedCallback(
+ 'clientbound',
+ meta,
+ data,
+ id,
+ true,
+ true
+ )
}
+
+ return true
}
-exports.writeToServer = function (meta, data, noCallback) {
+exports.writeToServer = function (
+ meta,
+ data,
+ noCallback
+) {
+ if (!realServer) {
+ return false
+ }
+
if (typeof meta === 'string') {
- meta = { name: meta }
+ meta = {
+ name: meta,
+ state: states.PLAY
+ }
}
- realServer.write(meta.name, data)
- const id = Object.keys(toServerMappings).find(key => toServerMappings[key] === meta.name)
- if (!noCallback) {
- storedCallback('serverbound', meta, data, id)
+
+ try {
+ realServer.write(meta.name, data)
+ } catch (error) {
+ return false
}
+
+ const id = getMappingId(
+ toServerMappings,
+ meta.name
+ )
+
+ if (
+ !noCallback &&
+ typeof storedCallback === 'function'
+ ) {
+ storedCallback(
+ 'serverbound',
+ meta,
+ data,
+ id,
+ true,
+ true
+ )
+ }
+
+ return true
}
-exports.setScriptingEnabled = function (isEnabled) {
- scriptingEnabled = isEnabled
-}
\ No newline at end of file
+exports.setScriptingEnabled = function (
+ isEnabled
+) {
+ scriptingEnabled = Boolean(isEnabled)
+}