Merge branch 'next' into instancing
This commit is contained in:
commit
7ec9d10787
58 changed files with 1323 additions and 613 deletions
|
|
@ -14,3 +14,5 @@ Ask AI
|
|||
- Some other global variables that can be used without window prefixes are listed in src/globals.d.ts
|
||||
|
||||
Rationale: This ensures a clean separation between the Mineflayer logic (server-side/game logic) and the renderer (client-side/view logic), making the renderer portable and testable, and maintains proper usage of global state.
|
||||
|
||||
For more general project contributing guides see CONTRIBUTING.md on like how to setup the project. Use pnpm tsc if needed to validate result with typechecking the whole project.
|
||||
|
|
|
|||
2
.github/workflows/build-single-file.yml
vendored
2
.github/workflows/build-single-file.yml
vendored
|
|
@ -23,6 +23,8 @@ jobs:
|
|||
|
||||
- name: Build single-file version - minecraft.html
|
||||
run: pnpm build-single-file && mv dist/single/index.html minecraft.html
|
||||
env:
|
||||
LOCAL_CONFIG_FILE: config.mcraft-only.json
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
|
|
|
|||
2
.github/workflows/build-zip.yml
vendored
2
.github/workflows/build-zip.yml
vendored
|
|
@ -23,6 +23,8 @@ jobs:
|
|||
|
||||
- name: Build project
|
||||
run: pnpm build
|
||||
env:
|
||||
LOCAL_CONFIG_FILE: config.mcraft-only.json
|
||||
|
||||
- name: Bundle server.js
|
||||
run: |
|
||||
|
|
|
|||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
|
@ -33,7 +33,7 @@ jobs:
|
|||
cd package
|
||||
zip -r ../self-host.zip .
|
||||
- run: pnpm build-playground
|
||||
- run: pnpm build-storybook
|
||||
# - run: pnpm build-storybook
|
||||
- run: pnpm test-unit
|
||||
- run: pnpm lint
|
||||
|
||||
|
|
|
|||
2
.github/workflows/next-deploy.yml
vendored
2
.github/workflows/next-deploy.yml
vendored
|
|
@ -36,7 +36,7 @@ jobs:
|
|||
run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
|
||||
env:
|
||||
CONFIG_JSON_SOURCE: BUNDLED
|
||||
- run: pnpm build-storybook
|
||||
LOCAL_CONFIG_FILE: config.mcraft-only.json
|
||||
- name: Copy playground files
|
||||
run: |
|
||||
mkdir -p .vercel/output/static/playground
|
||||
|
|
|
|||
2
.github/workflows/preview.yml
vendored
2
.github/workflows/preview.yml
vendored
|
|
@ -78,7 +78,7 @@ jobs:
|
|||
run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
|
||||
env:
|
||||
CONFIG_JSON_SOURCE: BUNDLED
|
||||
- run: pnpm build-storybook
|
||||
LOCAL_CONFIG_FILE: config.mcraft-only.json
|
||||
- name: Copy playground files
|
||||
run: |
|
||||
mkdir -p .vercel/output/static/playground
|
||||
|
|
|
|||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
|
|
@ -34,7 +34,7 @@ jobs:
|
|||
- run: vercel build --token=${{ secrets.VERCEL_TOKEN }} --prod
|
||||
env:
|
||||
CONFIG_JSON_SOURCE: BUNDLED
|
||||
- run: pnpm build-storybook
|
||||
LOCAL_CONFIG_FILE: config.mcraft-only.json
|
||||
- name: Copy playground files
|
||||
run: |
|
||||
mkdir -p .vercel/output/static/playground
|
||||
|
|
@ -48,12 +48,12 @@ jobs:
|
|||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: .vercel/output/static
|
||||
force_orphan: true
|
||||
|
||||
|
||||
- name: Change index.html title
|
||||
run: |
|
||||
# change <title>Minecraft Web Client</title> to <title>Minecraft Web Client — Free Online Browser Version</title>
|
||||
sed -i 's/<title>Minecraft Web Client<\/title>/<title>Minecraft Web Client — Free Online Browser Version<\/title>/' .vercel/output/static/index.html
|
||||
|
||||
|
||||
- name: Deploy Project to Vercel
|
||||
uses: mathiasvr/command-output@v2.0.0
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -177,8 +177,13 @@ New React components, improve UI (including mobile support).
|
|||
|
||||
## Updating Dependencies
|
||||
|
||||
1. Ensure mineflayer fork is up to date with the latest version of mineflayer original repo
|
||||
1. Use `pnpm update-git-deps` to check and update git dependencies (like mineflayer fork, prismarine packages etc). The script will:
|
||||
- Show which git dependencies have updates available
|
||||
- Ask if you want to update them
|
||||
- Skip dependencies listed in `pnpm.updateConfig.ignoreDependencies`
|
||||
|
||||
2. Update PrismarineJS dependencies to the latest version: `minecraft-data` (be sure to replace the version twice in the package.json), `mineflayer`, `minecraft-protocol`, `prismarine-block`, `prismarine-chunk`, `prismarine-item`, ...
|
||||
|
||||
3. If `minecraft-protocol` patch fails, do this:
|
||||
1. Remove the patch from `patchedDependencies` in `package.json`
|
||||
2. Run `pnpm patch minecraft-protocol`, open patch directory
|
||||
|
|
|
|||
14
README.MD
14
README.MD
|
|
@ -12,6 +12,9 @@ Don't confuse with [Eaglercraft](https://git.eaglercraft.rip/eaglercraft/eaglerc
|
|||
|
||||
For building the project yourself / contributing, see [Development, Debugging & Contributing](#development-debugging--contributing). For reference at what and how web technologies / frameworks are used, see [TECH.md](./TECH.md) (also for comparison with Eaglercraft).
|
||||
|
||||
> **Note**: You can deploy it on your own server in less than a minute using a one-liner script from [Minecraft Everywhere repo](https://github.com/zardoy/minecraft-everywhere)
|
||||
|
||||
|
||||
### Big Features
|
||||
|
||||
- Official Mineflayer [plugin integration](https://github.com/zardoy/mcraft-fun-mineflayer-plugin)! View / Control your bot remotely.
|
||||
|
|
@ -30,7 +33,7 @@ For building the project yourself / contributing, see [Development, Debugging &
|
|||
- Support for custom rendering 3D engines. Modular architecture.
|
||||
- even even more!
|
||||
|
||||
All components that are in [Storybook](https://mcraft.fun/storybook) are published as npm module and can be used in other projects: [`minecraft-react`](https://npmjs.com/minecraft-react)
|
||||
All components that are in [Storybook](https://minimap.mcraft.fun/storybook/) are published as npm module and can be used in other projects: [`minecraft-react`](https://npmjs.com/minecraft-react)
|
||||
|
||||
### Recommended Settings
|
||||
|
||||
|
|
@ -42,9 +45,12 @@ All components that are in [Storybook](https://mcraft.fun/storybook) are publish
|
|||
|
||||
### Browser Notes
|
||||
|
||||
These browsers have issues with capturing pointer:
|
||||
This project is tested with BrowserStack. Special thanks to [BrowserStack](https://www.browserstack.com/) for providing testing infrastructure!
|
||||
|
||||
Howerver, it's known that these browsers have issues:
|
||||
|
||||
**Opera Mini**: Disable *mouse gestures* in browsre settings to avoid opening new tab on right click hold
|
||||
|
||||
**Vivaldi**: Disable Controls -> *Raw Input* in game settings if experiencing issues
|
||||
|
||||
### Versions Support
|
||||
|
|
@ -124,7 +130,7 @@ Instead I recommend setting `options.debugLogNotFrequentPackets`. Also you can u
|
|||
|
||||
- `bot` - Mineflayer bot instance. See Mineflayer documentation for more.
|
||||
- `viewer` - Three.js viewer instance, basically does all the rendering.
|
||||
- `viewer.world.sectionObjects` - Object with all active chunk sections (geometries) in the world. Each chunk section is a Three.js mesh or group.
|
||||
- `world.sectionObjects` - Object with all active chunk sections (geometries) in the world. Each chunk section is a Three.js mesh or group.
|
||||
- `debugSceneChunks` - The same as above, but relative to current bot position (e.g. 0,0 is the current chunk).
|
||||
- `debugChangedOptions` - See what options are changed. Don't change options here.
|
||||
- `localServer`/`server` - Only for singleplayer mode/host. Flying Squid server instance, see it's documentation for more.
|
||||
|
|
@ -133,7 +139,7 @@ Instead I recommend setting `options.debugLogNotFrequentPackets`. Also you can u
|
|||
|
||||
- `nbt.simplify(someNbt)` - Simplifies nbt data, so it's easier to read.
|
||||
|
||||
The most useful thing in devtools is the watch expression. You can add any expression there and it will be re-evaluated in real time. For example, you can add `viewer.camera.position` to see the camera position and so on.
|
||||
The most useful thing in devtools is the watch expression. You can add any expression there and it will be re-evaluated in real time. For example, you can add `camera.position` to see the camera position and so on.
|
||||
|
||||
<img src="./docs-assets/watch-expr.png" alt="Watch expression" width="480"/>
|
||||
|
||||
|
|
|
|||
237
assets/debug-inputs.html
Normal file
237
assets/debug-inputs.html
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Web Input Debugger</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 20px;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
.key-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 60px);
|
||||
gap: 5px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.key {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border: 2px solid #333;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
background: white;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
}
|
||||
.key.pressed {
|
||||
background: #90EE90;
|
||||
}
|
||||
.key .duration {
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
font-size: 10px;
|
||||
}
|
||||
.key .count {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
font-size: 10px;
|
||||
}
|
||||
.controls {
|
||||
margin: 20px 0;
|
||||
padding: 10px;
|
||||
background: white;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.wasd-container {
|
||||
position: relative;
|
||||
width: 190px;
|
||||
height: 130px;
|
||||
}
|
||||
#KeyW {
|
||||
position: absolute;
|
||||
left: 65px;
|
||||
top: 0;
|
||||
}
|
||||
#KeyA {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 65px;
|
||||
}
|
||||
#KeyS {
|
||||
position: absolute;
|
||||
left: 65px;
|
||||
top: 65px;
|
||||
}
|
||||
#KeyD {
|
||||
position: absolute;
|
||||
left: 130px;
|
||||
top: 65px;
|
||||
}
|
||||
.space-container {
|
||||
margin-top: 20px;
|
||||
}
|
||||
#Space {
|
||||
width: 190px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="controls">
|
||||
<label>
|
||||
<input type="checkbox" id="repeatMode"> Use keydown repeat mode (auto key-up after 150ms of no repeat)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="wasd-container">
|
||||
<div id="KeyW" class="key" data-code="KeyW">W</div>
|
||||
<div id="KeyA" class="key" data-code="KeyA">A</div>
|
||||
<div id="KeyS" class="key" data-code="KeyS">S</div>
|
||||
<div id="KeyD" class="key" data-code="KeyD">D</div>
|
||||
</div>
|
||||
|
||||
<div class="key-container">
|
||||
<div id="ControlLeft" class="key" data-code="ControlLeft">Ctrl</div>
|
||||
</div>
|
||||
|
||||
<div class="space-container">
|
||||
<div id="Space" class="key" data-code="Space">Space</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const keys = {};
|
||||
const keyStats = {};
|
||||
const pressStartTimes = {};
|
||||
const keyTimeouts = {};
|
||||
|
||||
function initKeyStats(code) {
|
||||
if (!keyStats[code]) {
|
||||
keyStats[code] = {
|
||||
pressCount: 0,
|
||||
duration: 0,
|
||||
startTime: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function updateKeyVisuals(code) {
|
||||
const element = document.getElementById(code);
|
||||
if (!element) return;
|
||||
|
||||
const stats = keyStats[code];
|
||||
if (keys[code]) {
|
||||
element.classList.add('pressed');
|
||||
const currentDuration = ((Date.now() - stats.startTime) / 1000).toFixed(1);
|
||||
element.innerHTML = `${element.getAttribute('data-code').replace('Key', '').replace('Left', '')}<span class="duration">${currentDuration}s</span><span class="count">${stats.pressCount}</span>`;
|
||||
} else {
|
||||
element.classList.remove('pressed');
|
||||
element.innerHTML = `${element.getAttribute('data-code').replace('Key', '').replace('Left', '')}<span class="count">${stats.pressCount}</span>`;
|
||||
}
|
||||
}
|
||||
|
||||
function releaseKey(code) {
|
||||
keys[code] = false;
|
||||
if (pressStartTimes[code]) {
|
||||
keyStats[code].duration += (Date.now() - pressStartTimes[code]) / 1000;
|
||||
delete pressStartTimes[code];
|
||||
}
|
||||
updateKeyVisuals(code);
|
||||
}
|
||||
|
||||
function handleKeyDown(event) {
|
||||
const code = event.code;
|
||||
const isRepeatMode = document.getElementById('repeatMode').checked;
|
||||
|
||||
initKeyStats(code);
|
||||
|
||||
// Clear any existing timeout for this key
|
||||
if (keyTimeouts[code]) {
|
||||
clearTimeout(keyTimeouts[code]);
|
||||
delete keyTimeouts[code];
|
||||
}
|
||||
|
||||
if (isRepeatMode) {
|
||||
// In repeat mode, always handle the keydown
|
||||
if (!keys[code] || event.repeat) {
|
||||
keys[code] = true;
|
||||
if (!event.repeat) {
|
||||
// Only increment count on initial press, not repeats
|
||||
keyStats[code].pressCount++;
|
||||
keyStats[code].startTime = Date.now();
|
||||
pressStartTimes[code] = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
// Set timeout to release key if no repeat events come
|
||||
keyTimeouts[code] = setTimeout(() => {
|
||||
releaseKey(code);
|
||||
}, 150);
|
||||
} else {
|
||||
// In normal mode, only handle keydown if key is not already pressed
|
||||
if (!keys[code]) {
|
||||
keys[code] = true;
|
||||
keyStats[code].pressCount++;
|
||||
keyStats[code].startTime = Date.now();
|
||||
pressStartTimes[code] = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
updateKeyVisuals(code);
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
function handleKeyUp(event) {
|
||||
const code = event.code;
|
||||
const isRepeatMode = document.getElementById('repeatMode').checked;
|
||||
|
||||
if (!isRepeatMode) {
|
||||
releaseKey(code);
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
// Initialize all monitored keys
|
||||
const monitoredKeys = ['KeyW', 'KeyA', 'KeyS', 'KeyD', 'ControlLeft', 'Space'];
|
||||
monitoredKeys.forEach(code => {
|
||||
initKeyStats(code);
|
||||
const element = document.getElementById(code);
|
||||
if (element) {
|
||||
element.innerHTML = `${element.getAttribute('data-code').replace('Key', '').replace('Left', '')}<span class="count">0</span>`;
|
||||
}
|
||||
});
|
||||
|
||||
// Start visual updates
|
||||
setInterval(() => {
|
||||
monitoredKeys.forEach(code => {
|
||||
if (keys[code]) {
|
||||
updateKeyVisuals(code);
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
|
||||
// Event listeners
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
document.addEventListener('keyup', handleKeyUp);
|
||||
|
||||
// Handle mode changes
|
||||
document.getElementById('repeatMode').addEventListener('change', () => {
|
||||
// Release all keys when switching modes
|
||||
monitoredKeys.forEach(code => {
|
||||
if (keys[code]) {
|
||||
releaseKey(code);
|
||||
}
|
||||
if (keyTimeouts[code]) {
|
||||
clearTimeout(keyTimeouts[code]);
|
||||
delete keyTimeouts[code];
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -3,12 +3,16 @@
|
|||
"defaultHost": "<from-proxy>",
|
||||
"defaultProxy": "https://proxy.mcraft.fun",
|
||||
"mapsProvider": "https://maps.mcraft.fun/",
|
||||
"skinTexturesProxy": "",
|
||||
"peerJsServer": "",
|
||||
"peerJsServerFallback": "https://p2p.mcraft.fun",
|
||||
"promoteServers": [
|
||||
{
|
||||
"ip": "wss://play.mcraft.fun"
|
||||
},
|
||||
{
|
||||
"ip": "wss://ws.fuchsmc.net"
|
||||
},
|
||||
{
|
||||
"ip": "wss://play2.mcraft.fun"
|
||||
},
|
||||
|
|
|
|||
4
config.mcraft-only.json
Normal file
4
config.mcraft-only.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"alwaysReconnectButton": true,
|
||||
"reportBugButtonWithReconnect": true
|
||||
}
|
||||
|
|
@ -1,101 +1,61 @@
|
|||
import * as THREE from 'three'
|
||||
import * as tweenJs from '@tweenjs/tween.js'
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
|
||||
import * as THREE from 'three';
|
||||
import Jimp from 'jimp';
|
||||
import { loadThreeJsTextureFromBitmap } from '../renderer/viewer/lib/utils/skins'
|
||||
|
||||
// Create scene, camera and renderer
|
||||
const scene = new THREE.Scene()
|
||||
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
|
||||
camera.position.set(0, 0, 5)
|
||||
const renderer = new THREE.WebGLRenderer()
|
||||
renderer.setSize(window.innerWidth, window.innerHeight)
|
||||
document.body.appendChild(renderer.domElement)
|
||||
|
||||
const controls = new OrbitControls(camera, renderer.domElement)
|
||||
// Position camera
|
||||
camera.position.z = 5
|
||||
|
||||
const geometry = new THREE.BoxGeometry(1, 1, 1)
|
||||
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
|
||||
const cube = new THREE.Mesh(geometry, material)
|
||||
cube.position.set(0.5, 0.5, 0.5);
|
||||
const group = new THREE.Group()
|
||||
group.add(cube)
|
||||
group.position.set(-0.5, -0.5, -0.5);
|
||||
const outerGroup = new THREE.Group()
|
||||
outerGroup.add(group)
|
||||
outerGroup.scale.set(0.2, 0.2, 0.2)
|
||||
outerGroup.position.set(1, 1, 0)
|
||||
scene.add(outerGroup)
|
||||
// Create a canvas with some content
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = 256
|
||||
canvas.height = 256
|
||||
const ctx = canvas.getContext('2d')
|
||||
|
||||
// const mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0x00_00_ff, transparent: true, opacity: 0.5 }))
|
||||
// mesh.position.set(0.5, 1, 0.5)
|
||||
// const group = new THREE.Group()
|
||||
// group.add(mesh)
|
||||
// group.position.set(-0.5, -1, -0.5)
|
||||
// const outerGroup = new THREE.Group()
|
||||
// outerGroup.add(group)
|
||||
// // outerGroup.position.set(this.camera.position.x, this.camera.position.y, this.camera.position.z)
|
||||
// scene.add(outerGroup)
|
||||
scene.background = new THREE.Color(0x444444)
|
||||
|
||||
new tweenJs.Tween(group.rotation).to({ z: THREE.MathUtils.degToRad(90) }, 1000).yoyo(true).repeat(Infinity).start()
|
||||
// Draw something on the canvas
|
||||
ctx.fillStyle = '#444444'
|
||||
// ctx.fillRect(0, 0, 256, 256)
|
||||
ctx.fillStyle = 'red'
|
||||
ctx.font = '48px Arial'
|
||||
ctx.textAlign = 'center'
|
||||
ctx.textBaseline = 'middle'
|
||||
ctx.fillText('Hello!', 128, 128)
|
||||
|
||||
const tweenGroup = new tweenJs.Group()
|
||||
function animate () {
|
||||
tweenGroup.update()
|
||||
requestAnimationFrame(animate)
|
||||
// cube.rotation.x += 0.01
|
||||
// cube.rotation.y += 0.01
|
||||
renderer.render(scene, camera)
|
||||
// Create bitmap and texture
|
||||
async function createTexturedBox() {
|
||||
const canvas2 = new OffscreenCanvas(256, 256)
|
||||
const ctx2 = canvas2.getContext('2d')!
|
||||
ctx2.drawImage(canvas, 0, 0)
|
||||
const texture = new THREE.Texture(canvas2)
|
||||
texture.magFilter = THREE.NearestFilter
|
||||
texture.minFilter = THREE.NearestFilter
|
||||
texture.needsUpdate = true
|
||||
texture.flipY = false
|
||||
|
||||
// Create box with texture
|
||||
const geometry = new THREE.BoxGeometry(2, 2, 2)
|
||||
const material = new THREE.MeshBasicMaterial({
|
||||
map: texture,
|
||||
side: THREE.DoubleSide,
|
||||
premultipliedAlpha: false,
|
||||
})
|
||||
const cube = new THREE.Mesh(geometry, material)
|
||||
scene.add(cube)
|
||||
}
|
||||
|
||||
// Create the textured box
|
||||
createTexturedBox()
|
||||
|
||||
// Animation loop
|
||||
function animate() {
|
||||
requestAnimationFrame(animate)
|
||||
renderer.render(scene, camera)
|
||||
}
|
||||
animate()
|
||||
|
||||
// let animation
|
||||
|
||||
window.animate = () => {
|
||||
// new Tween.Tween(group.position).to({ y: group.position.y - 1}, 1000 * 0.35/2).yoyo(true).repeat(1).start()
|
||||
new tweenJs.Tween(group.rotation, tweenGroup).to({ z: THREE.MathUtils.degToRad(90) }, 1000 * 0.35 / 2).yoyo(true).repeat(Infinity).start().onRepeat(() => {
|
||||
console.log('done')
|
||||
})
|
||||
}
|
||||
|
||||
window.stop = () => {
|
||||
tweenGroup.removeAll()
|
||||
}
|
||||
|
||||
|
||||
function createGeometryFromImage() {
|
||||
return new Promise<THREE.ShapeGeometry>((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABEElEQVQ4jWNkIAPw2Zv9J0cfXPOSvx/+L/n74T+HqsJ/JlI1T9u3i6H91B7ybdY+vgZuO1majV+fppFmPnuz/+ihy2dv9t/49Wm8mlECkV1FHh5FfPZm/1XXTGX4cechA4eKPMNVq1CGH7cfMBJ0rlxX+X8OVYX/xq9P/5frKifoZ0Z0AwS8HRkYGBgYvt+8xyDXUUbQZgwJPnuz/+wq8gw/7zxk+PXsFUFno0h6mon+l5fgZFhwnYmBTUqMgYGBgaAhLMiaHQyFGOZvf8Lw49FXRgYGhv8MDAwwg/7jMoQFFury/C8Y5m9/wnADohnZVryJhoWBARJ9Cw69gtmMAgiFAcuvZ68Yfj17hU8NXgAATdKfkzbQhBEAAAAASUVORK5CYII='
|
||||
console.log('img.complete', img.complete)
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
const context = canvas.getContext('2d');
|
||||
context.drawImage(img, 0, 0, img.width, img.height);
|
||||
const imgData = context.getImageData(0, 0, img.width, img.height);
|
||||
|
||||
const shape = new THREE.Shape();
|
||||
for (let y = 0; y < img.height; y++) {
|
||||
for (let x = 0; x < img.width; x++) {
|
||||
const index = (y * img.width + x) * 4;
|
||||
const alpha = imgData.data[index + 3];
|
||||
if (alpha !== 0) {
|
||||
shape.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const geometry = new THREE.ShapeGeometry(shape);
|
||||
resolve(geometry);
|
||||
};
|
||||
img.onerror = reject;
|
||||
});
|
||||
}
|
||||
|
||||
// Usage:
|
||||
const shapeGeomtry = createGeometryFromImage().then(geometry => {
|
||||
const material = new THREE.MeshBasicMaterial({ color: 0xffffff });
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
scene.add(mesh);
|
||||
})
|
||||
|
|
|
|||
23
index.html
23
index.html
|
|
@ -27,6 +27,7 @@
|
|||
<div style="font-size: var(--font-size);color: rgb(176, 176, 176);margin-top: 3px;text-align: center" class="subtitle">A true Minecraft client in your browser!</div>
|
||||
<!-- small text pre -->
|
||||
<div style="font-size: calc(var(--font-size) * 0.6);color: rgb(150, 150, 150);margin-top: 3px;text-align: center;white-space: pre-line;" class="advanced-info"></div>
|
||||
<div style="font-size: calc(var(--font-size) * 0.6);color: rgb(255, 100, 100);margin-top: 10px;text-align: center;display: none;" class="ios-warning">Only iOS 15+ is supported due to performance optimizations</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
|
@ -36,6 +37,13 @@
|
|||
if (!window.pageLoaded) {
|
||||
document.documentElement.appendChild(loadingDivElem)
|
||||
}
|
||||
|
||||
// iOS version detection
|
||||
const getIOSVersion = () => {
|
||||
const match = navigator.userAgent.match(/OS (\d+)_(\d+)_?(\d+)?/);
|
||||
return match ? parseInt(match[1], 10) : null;
|
||||
}
|
||||
|
||||
// load error handling
|
||||
const onError = (errorOrMessage, log = false) => {
|
||||
let message = errorOrMessage instanceof Error ? (errorOrMessage.stack ?? errorOrMessage.message) : errorOrMessage
|
||||
|
|
@ -46,12 +54,23 @@
|
|||
const [errorMessage, ...errorStack] = message.split('\n')
|
||||
document.querySelector('.initial-loader').querySelector('.subtitle').textContent = errorMessage
|
||||
document.querySelector('.initial-loader').querySelector('.advanced-info').textContent = errorStack.join('\n')
|
||||
|
||||
// Show iOS warning if applicable
|
||||
const iosVersion = getIOSVersion();
|
||||
if (iosVersion !== null && iosVersion < 15) {
|
||||
document.querySelector('.initial-loader').querySelector('.ios-warning').style.display = 'block';
|
||||
}
|
||||
|
||||
if (window.navigator.maxTouchPoints > 1) window.location.hash = '#dev' // show eruda
|
||||
// unregister all sw
|
||||
if (window.navigator.serviceWorker) {
|
||||
if (window.navigator.serviceWorker && document.querySelector('.initial-loader').style.opacity !== 0) {
|
||||
console.log('got worker')
|
||||
window.navigator.serviceWorker.getRegistrations().then(registrations => {
|
||||
registrations.forEach(registration => {
|
||||
registration.unregister()
|
||||
console.log('got registration')
|
||||
registration.unregister().then(() => {
|
||||
console.log('worker unregistered')
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
|||
18
package.json
18
package.json
|
|
@ -7,6 +7,7 @@
|
|||
"dev-proxy": "node server.js",
|
||||
"start": "run-p dev-proxy dev-rsbuild watch-mesher",
|
||||
"start2": "run-p dev-rsbuild watch-mesher",
|
||||
"start-metrics": "ENABLE_METRICS=true rsbuild dev",
|
||||
"build": "pnpm build-other-workers && rsbuild build",
|
||||
"build-analyze": "BUNDLE_ANALYZE=true rsbuild build && pnpm build-other-workers",
|
||||
"build-single-file": "SINGLE_FILE_BUILD=true rsbuild build",
|
||||
|
|
@ -31,7 +32,9 @@
|
|||
"run-playground": "run-p watch-mesher watch-other-workers watch-playground",
|
||||
"run-all": "run-p start run-playground",
|
||||
"build-playground": "rsbuild build --config renderer/rsbuild.config.ts",
|
||||
"watch-playground": "rsbuild dev --config renderer/rsbuild.config.ts"
|
||||
"watch-playground": "rsbuild dev --config renderer/rsbuild.config.ts",
|
||||
"update-git-deps": "tsx scripts/updateGitDeps.ts",
|
||||
"request-data": "tsx scripts/requestData.ts"
|
||||
},
|
||||
"keywords": [
|
||||
"prismarine",
|
||||
|
|
@ -83,7 +86,7 @@
|
|||
"jszip": "^3.10.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mcraft-fun-mineflayer": "^0.1.23",
|
||||
"minecraft-data": "3.89.0",
|
||||
"minecraft-data": "3.92.0",
|
||||
"minecraft-protocol": "github:PrismarineJS/node-minecraft-protocol#master",
|
||||
"mineflayer-item-map-downloader": "github:zardoy/mineflayer-item-map-downloader",
|
||||
"mojangson": "^2.0.4",
|
||||
|
|
@ -150,10 +153,10 @@
|
|||
"http-browserify": "^1.7.0",
|
||||
"http-server": "^14.1.1",
|
||||
"https-browserify": "^1.0.0",
|
||||
"mc-assets": "^0.2.59",
|
||||
"mc-assets": "^0.2.62",
|
||||
"minecraft-inventory-gui": "github:zardoy/minecraft-inventory-gui#next",
|
||||
"mineflayer": "github:zardoy/mineflayer#gen-the-master",
|
||||
"mineflayer-mouse": "^0.1.10",
|
||||
"mineflayer-mouse": "^0.1.11",
|
||||
"mineflayer-pathfinder": "^2.4.4",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"os-browserify": "^0.3.0",
|
||||
|
|
@ -201,7 +204,7 @@
|
|||
"diamond-square": "github:zardoy/diamond-square",
|
||||
"prismarine-block": "github:zardoy/prismarine-block#next-era",
|
||||
"prismarine-world": "github:zardoy/prismarine-world#next-era",
|
||||
"minecraft-data": "3.89.0",
|
||||
"minecraft-data": "3.92.0",
|
||||
"prismarine-provider-anvil": "github:zardoy/prismarine-provider-anvil#everything",
|
||||
"prismarine-physics": "github:zardoy/prismarine-physics",
|
||||
"minecraft-protocol": "github:PrismarineJS/node-minecraft-protocol#master",
|
||||
|
|
@ -210,7 +213,10 @@
|
|||
"prismarine-item": "latest"
|
||||
},
|
||||
"updateConfig": {
|
||||
"ignoreDependencies": []
|
||||
"ignoreDependencies": [
|
||||
"browserfs",
|
||||
"google-drive-browserfs"
|
||||
]
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"pixelarticons@1.8.1": "patches/pixelarticons@1.8.1.patch",
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
diff --git a/src/client/chat.js b/src/client/chat.js
|
||||
index f14269bea055d4329cd729271e7406ec4b344de7..00f5482eb6e3c911381ca9a728b1b4aae0d1d337 100644
|
||||
index 8d0869b150681574ad19292a026cce9f67a137ee..2efa2e6600f017b566155974cb9fb1856fa582f9 100644
|
||||
--- a/src/client/chat.js
|
||||
+++ b/src/client/chat.js
|
||||
@@ -111,7 +111,7 @@ module.exports = function (client, options) {
|
||||
for (const player of packet.data) {
|
||||
if (!player.chatSession) continue
|
||||
client._players[player.UUID] = {
|
||||
- publicKey: crypto.createPublicKey({ key: player.chatSession.publicKey.keyBytes, format: 'der', type: 'spki' }),
|
||||
+ // publicKey: crypto.createPublicKey({ key: player.chatSession.publicKey.keyBytes, format: 'der', type: 'spki' }),
|
||||
publicKeyDER: player.chatSession.publicKey.keyBytes,
|
||||
sessionUuid: player.chatSession.uuid
|
||||
}
|
||||
@@ -127,7 +127,7 @@ module.exports = function (client, options) {
|
||||
for (const player of packet.data) {
|
||||
if (player.crypto) {
|
||||
client._players[player.UUID] = {
|
||||
- publicKey: crypto.createPublicKey({ key: player.crypto.publicKey, format: 'der', type: 'spki' }),
|
||||
+ // publicKey: crypto.createPublicKey({ key: player.crypto.publicKey, format: 'der', type: 'spki' }),
|
||||
publicKeyDER: player.crypto.publicKey,
|
||||
signature: player.crypto.signature,
|
||||
displayName: player.displayName || player.name
|
||||
@@ -198,7 +198,7 @@ module.exports = function (client, options) {
|
||||
@@ -109,7 +109,7 @@ module.exports = function (client, options) {
|
||||
for (const player of packet.data) {
|
||||
if (player.chatSession) {
|
||||
client._players[player.uuid] = {
|
||||
- publicKey: crypto.createPublicKey({ key: player.chatSession.publicKey.keyBytes, format: 'der', type: 'spki' }),
|
||||
+ // publicKey: crypto.createPublicKey({ key: player.chatSession.publicKey.keyBytes, format: 'der', type: 'spki' }),
|
||||
publicKeyDER: player.chatSession.publicKey.keyBytes,
|
||||
sessionUuid: player.chatSession.uuid
|
||||
}
|
||||
@@ -119,7 +119,7 @@ module.exports = function (client, options) {
|
||||
|
||||
if (player.crypto) {
|
||||
client._players[player.uuid] = {
|
||||
- publicKey: crypto.createPublicKey({ key: player.crypto.publicKey, format: 'der', type: 'spki' }),
|
||||
+ // publicKey: crypto.createPublicKey({ key: player.crypto.publicKey, format: 'der', type: 'spki' }),
|
||||
publicKeyDER: player.crypto.publicKey,
|
||||
signature: player.crypto.signature,
|
||||
displayName: player.displayName || player.name
|
||||
@@ -189,7 +189,7 @@ module.exports = function (client, options) {
|
||||
if (mcData.supportFeature('useChatSessions')) {
|
||||
const tsDelta = BigInt(Date.now()) - packet.timestamp
|
||||
const expired = !packet.timestamp || tsDelta > messageExpireTime || tsDelta < 0
|
||||
|
|
@ -29,7 +29,7 @@ index f14269bea055d4329cd729271e7406ec4b344de7..00f5482eb6e3c911381ca9a728b1b4aa
|
|||
if (verified) client._signatureCache.push(packet.signature)
|
||||
client.emit('playerChat', {
|
||||
plainMessage: packet.plainMessage,
|
||||
@@ -363,7 +363,7 @@ module.exports = function (client, options) {
|
||||
@@ -354,7 +354,7 @@ module.exports = function (client, options) {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ index f14269bea055d4329cd729271e7406ec4b344de7..00f5482eb6e3c911381ca9a728b1b4aa
|
|||
options.timestamp = options.timestamp || BigInt(Date.now())
|
||||
options.salt = options.salt || 1n
|
||||
|
||||
@@ -405,7 +405,7 @@ module.exports = function (client, options) {
|
||||
@@ -396,7 +396,7 @@ module.exports = function (client, options) {
|
||||
message,
|
||||
timestamp: options.timestamp,
|
||||
salt: options.salt,
|
||||
|
|
@ -47,7 +47,7 @@ index f14269bea055d4329cd729271e7406ec4b344de7..00f5482eb6e3c911381ca9a728b1b4aa
|
|||
offset: client._lastSeenMessages.pending,
|
||||
acknowledged
|
||||
})
|
||||
@@ -419,7 +419,7 @@ module.exports = function (client, options) {
|
||||
@@ -410,7 +410,7 @@ module.exports = function (client, options) {
|
||||
message,
|
||||
timestamp: options.timestamp,
|
||||
salt: options.salt,
|
||||
|
|
@ -74,7 +74,7 @@ index b9d21bab9faccd5dbf1975fc423fc55c73e906c5..99ffd76527b410e3a393181beb260108
|
|||
|
||||
function onJoinServerResponse (err) {
|
||||
diff --git a/src/client/play.js b/src/client/play.js
|
||||
index 6e06dc15291b38e1eeeec8d7102187b2a23d70a3..f67454942db9276cbb9eab99c281cfe182cb8a1f 100644
|
||||
index 559607f34e9a5b2b7809423f8ca4cd6746b60225..4dc1c3139438cc2729b05c57e57bd00252728f8a 100644
|
||||
--- a/src/client/play.js
|
||||
+++ b/src/client/play.js
|
||||
@@ -53,7 +53,7 @@ module.exports = function (client, options) {
|
||||
|
|
@ -87,7 +87,7 @@ index 6e06dc15291b38e1eeeec8d7102187b2a23d70a3..f67454942db9276cbb9eab99c281cfe1
|
|||
})
|
||||
// Server should send finish_configuration on its own right after sending the client a dimension codec
|
||||
diff --git a/src/client.js b/src/client.js
|
||||
index 74749698f8cee05b5dc749c271544f78d06645b0..e77e0a3f41c1ee780c3abbd54b0801d248c2a07c 100644
|
||||
index 5c7a62b013daa69be91ec9e763b1f48ffe96ffa6..174d42a77740a937afcb106e1f39a9ee824a24b9 100644
|
||||
--- a/src/client.js
|
||||
+++ b/src/client.js
|
||||
@@ -89,10 +89,12 @@ class Client extends EventEmitter {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
diff --git a/fonts/pixelart-icons-font.css b/fonts/pixelart-icons-font.css
|
||||
index 3b2ebe839370d96bf93ef5ca94a827f07e49378d..103ab4d6b9f3b5c9f41d1407e3cbf4ac392fbf41 100644
|
||||
index 3b2ebe839370d96bf93ef5ca94a827f07e49378d..4f8d76be2ca6e4ddc43c68d0a6f0f69979165ab4 100644
|
||||
--- a/fonts/pixelart-icons-font.css
|
||||
+++ b/fonts/pixelart-icons-font.css
|
||||
@@ -1,16 +1,13 @@
|
||||
|
|
@ -10,10 +10,11 @@ index 3b2ebe839370d96bf93ef5ca94a827f07e49378d..103ab4d6b9f3b5c9f41d1407e3cbf4ac
|
|||
+ src:
|
||||
url("pixelart-icons-font.woff2?t=1711815892278") format("woff2"),
|
||||
url("pixelart-icons-font.woff?t=1711815892278") format("woff"),
|
||||
url('pixelart-icons-font.ttf?t=1711815892278') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
|
||||
- url('pixelart-icons-font.ttf?t=1711815892278') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
|
||||
- url('pixelart-icons-font.svg?t=1711815892278#pixelart-icons-font') format('svg'); /* iOS 4.1- */
|
||||
+ url('pixelart-icons-font.ttf?t=1711815892278') format('truetype'); /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
|
||||
}
|
||||
|
||||
|
||||
[class^="pixelart-icons-font-"], [class*=" pixelart-icons-font-"] {
|
||||
font-family: 'pixelart-icons-font' !important;
|
||||
- font-size:24px;
|
||||
|
|
|
|||
416
pnpm-lock.yaml
generated
416
pnpm-lock.yaml
generated
|
|
@ -12,7 +12,7 @@ overrides:
|
|||
diamond-square: github:zardoy/diamond-square
|
||||
prismarine-block: github:zardoy/prismarine-block#next-era
|
||||
prismarine-world: github:zardoy/prismarine-world#next-era
|
||||
minecraft-data: 3.89.0
|
||||
minecraft-data: 3.92.0
|
||||
prismarine-provider-anvil: github:zardoy/prismarine-provider-anvil#everything
|
||||
prismarine-physics: github:zardoy/prismarine-physics
|
||||
minecraft-protocol: github:PrismarineJS/node-minecraft-protocol#master
|
||||
|
|
@ -22,13 +22,13 @@ overrides:
|
|||
|
||||
patchedDependencies:
|
||||
minecraft-protocol:
|
||||
hash: 09def7a73311f10b6aa8ff9f9b76129589578fa154a8b846b06909ca748c4762
|
||||
hash: a8726e6981ddc3486262d981d1e2030f379901c055ac9c4bf3036b4149e860e0
|
||||
path: patches/minecraft-protocol.patch
|
||||
mineflayer-item-map-downloader@1.2.0:
|
||||
hash: a731ebbace2d8790c973ab3a5ba33494a6e9658533a9710dd8ba36f86db061ad
|
||||
path: patches/mineflayer-item-map-downloader@1.2.0.patch
|
||||
pixelarticons@1.8.1:
|
||||
hash: d6a3d784047beba873565d1198bed425d9eb2de942e3fc8edac55f25473e4325
|
||||
hash: 533230072bc402f425c86abd3d0356fe087b14cab2a254d93f419b083f2d8dfa
|
||||
path: patches/pixelarticons@1.8.1.patch
|
||||
|
||||
importers:
|
||||
|
|
@ -136,13 +136,13 @@ importers:
|
|||
version: 4.17.21
|
||||
mcraft-fun-mineflayer:
|
||||
specifier: ^0.1.23
|
||||
version: 0.1.23(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/0380bed150fe03db4ac37f0194e0cee98356647d(encoding@0.1.13))
|
||||
version: 0.1.23(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/3daf1f4bdc6afad0dedd87b879875f3dbb7b0980(encoding@0.1.13))
|
||||
minecraft-data:
|
||||
specifier: 3.89.0
|
||||
version: 3.89.0
|
||||
specifier: 3.92.0
|
||||
version: 3.92.0
|
||||
minecraft-protocol:
|
||||
specifier: github:PrismarineJS/node-minecraft-protocol#master
|
||||
version: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/9e116c3dd4682b17c4e2c80249a2447a093d9284(patch_hash=09def7a73311f10b6aa8ff9f9b76129589578fa154a8b846b06909ca748c4762)(encoding@0.1.13)
|
||||
version: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/6c2204a813690ead420e2b8c7f0ef32ca357d176(patch_hash=a8726e6981ddc3486262d981d1e2030f379901c055ac9c4bf3036b4149e860e0)(encoding@0.1.13)
|
||||
mineflayer-item-map-downloader:
|
||||
specifier: github:zardoy/mineflayer-item-map-downloader
|
||||
version: https://codeload.github.com/zardoy/mineflayer-item-map-downloader/tar.gz/a8d210ecdcf78dd082fa149a96e1612cc9747824(patch_hash=a731ebbace2d8790c973ab3a5ba33494a6e9658533a9710dd8ba36f86db061ad)(encoding@0.1.13)
|
||||
|
|
@ -160,13 +160,13 @@ importers:
|
|||
version: 1.5.4
|
||||
pixelarticons:
|
||||
specifier: ^1.8.1
|
||||
version: 1.8.1(patch_hash=d6a3d784047beba873565d1198bed425d9eb2de942e3fc8edac55f25473e4325)
|
||||
version: 1.8.1(patch_hash=533230072bc402f425c86abd3d0356fe087b14cab2a254d93f419b083f2d8dfa)
|
||||
pretty-bytes:
|
||||
specifier: ^6.1.1
|
||||
version: 6.1.1
|
||||
prismarine-provider-anvil:
|
||||
specifier: github:zardoy/prismarine-provider-anvil#everything
|
||||
version: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/1d548fac63fe977c8281f0a9a522b37e4d92d0b7(minecraft-data@3.89.0)
|
||||
version: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/1d548fac63fe977c8281f0a9a522b37e4d92d0b7(minecraft-data@3.92.0)
|
||||
prosemirror-example-setup:
|
||||
specifier: ^1.2.2
|
||||
version: 1.2.3
|
||||
|
|
@ -334,17 +334,17 @@ importers:
|
|||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
mc-assets:
|
||||
specifier: ^0.2.59
|
||||
version: 0.2.59
|
||||
specifier: ^0.2.62
|
||||
version: 0.2.62
|
||||
minecraft-inventory-gui:
|
||||
specifier: github:zardoy/minecraft-inventory-gui#next
|
||||
version: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/5494f356b1e59eddc876c3ae05ff395f12a46379(@types/react@18.3.18)(react@18.3.1)
|
||||
version: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/89c33d396f3fde4804c71f4be3c203ade1833b41(@types/react@18.3.18)(react@18.3.1)
|
||||
mineflayer:
|
||||
specifier: github:zardoy/mineflayer#gen-the-master
|
||||
version: https://codeload.github.com/zardoy/mineflayer/tar.gz/0380bed150fe03db4ac37f0194e0cee98356647d(encoding@0.1.13)
|
||||
version: https://codeload.github.com/zardoy/mineflayer/tar.gz/3daf1f4bdc6afad0dedd87b879875f3dbb7b0980(encoding@0.1.13)
|
||||
mineflayer-mouse:
|
||||
specifier: ^0.1.10
|
||||
version: 0.1.10(@types/debug@4.1.12)(@types/node@22.13.9)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)
|
||||
specifier: ^0.1.11
|
||||
version: 0.1.11
|
||||
mineflayer-pathfinder:
|
||||
specifier: ^2.4.4
|
||||
version: 2.4.5
|
||||
|
|
@ -435,7 +435,7 @@ importers:
|
|||
version: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9
|
||||
prismarine-chunk:
|
||||
specifier: github:zardoy/prismarine-chunk#master
|
||||
version: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/c5feac83b61d95feb4d4f22c063dacfb8c192a9f(minecraft-data@3.89.0)
|
||||
version: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/c5feac83b61d95feb4d4f22c063dacfb8c192a9f(minecraft-data@3.92.0)
|
||||
prismarine-schematic:
|
||||
specifier: ^1.2.0
|
||||
version: 1.2.3
|
||||
|
|
@ -3308,47 +3308,18 @@ packages:
|
|||
'@vitest/expect@0.34.6':
|
||||
resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==}
|
||||
|
||||
'@vitest/expect@3.0.8':
|
||||
resolution: {integrity: sha512-Xu6TTIavTvSSS6LZaA3EebWFr6tsoXPetOWNMOlc7LO88QVVBwq2oQWBoDiLCN6YTvNYsGSjqOO8CAdjom5DCQ==}
|
||||
|
||||
'@vitest/mocker@3.0.8':
|
||||
resolution: {integrity: sha512-n3LjS7fcW1BCoF+zWZxG7/5XvuYH+lsFg+BDwwAz0arIwHQJFUEsKBQ0BLU49fCxuM/2HSeBPHQD8WjgrxMfow==}
|
||||
peerDependencies:
|
||||
msw: ^2.4.9
|
||||
vite: ^5.0.0 || ^6.0.0
|
||||
peerDependenciesMeta:
|
||||
msw:
|
||||
optional: true
|
||||
vite:
|
||||
optional: true
|
||||
|
||||
'@vitest/pretty-format@3.0.8':
|
||||
resolution: {integrity: sha512-BNqwbEyitFhzYMYHUVbIvepOyeQOSFA/NeJMIP9enMntkkxLgOcgABH6fjyXG85ipTgvero6noreavGIqfJcIg==}
|
||||
|
||||
'@vitest/runner@0.34.6':
|
||||
resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==}
|
||||
|
||||
'@vitest/runner@3.0.8':
|
||||
resolution: {integrity: sha512-c7UUw6gEcOzI8fih+uaAXS5DwjlBaCJUo7KJ4VvJcjL95+DSR1kova2hFuRt3w41KZEFcOEiq098KkyrjXeM5w==}
|
||||
|
||||
'@vitest/snapshot@0.34.6':
|
||||
resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==}
|
||||
|
||||
'@vitest/snapshot@3.0.8':
|
||||
resolution: {integrity: sha512-x8IlMGSEMugakInj44nUrLSILh/zy1f2/BgH0UeHpNyOocG18M9CWVIFBaXPt8TrqVZWmcPjwfG/ht5tnpba8A==}
|
||||
|
||||
'@vitest/spy@0.34.6':
|
||||
resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==}
|
||||
|
||||
'@vitest/spy@3.0.8':
|
||||
resolution: {integrity: sha512-MR+PzJa+22vFKYb934CejhR4BeRpMSoxkvNoDit68GQxRLSf11aT6CTj3XaqUU9rxgWJFnqicN/wxw6yBRkI1Q==}
|
||||
|
||||
'@vitest/utils@0.34.6':
|
||||
resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==}
|
||||
|
||||
'@vitest/utils@3.0.8':
|
||||
resolution: {integrity: sha512-nkBC3aEhfX2PdtQI/QwAWp8qZWwzASsU4Npbcd5RdMPBSSLCpkZp52P3xku3s3uA0HIEhGvEcF8rNkBsz9dQ4Q==}
|
||||
|
||||
'@xboxreplay/errors@0.1.0':
|
||||
resolution: {integrity: sha512-Tgz1d/OIPDWPeyOvuL5+aai5VCcqObhPnlI3skQuf80GVF3k1I0lPCnGC+8Cm5PV9aLBT5m8qPcJoIUQ2U4y9g==}
|
||||
|
||||
|
|
@ -3672,10 +3643,6 @@ packages:
|
|||
assertion-error@1.1.0:
|
||||
resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
|
||||
|
||||
assertion-error@2.0.1:
|
||||
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
assign-symbols@1.0.0:
|
||||
resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
|
@ -4012,10 +3979,6 @@ packages:
|
|||
resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
chai@5.2.0:
|
||||
resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
chalk@2.4.2:
|
||||
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
|
||||
engines: {node: '>=4'}
|
||||
|
|
@ -4043,10 +4006,6 @@ packages:
|
|||
check-error@1.0.3:
|
||||
resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}
|
||||
|
||||
check-error@2.1.1:
|
||||
resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
check-more-types@2.24.0:
|
||||
resolution: {integrity: sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
|
@ -4472,10 +4431,6 @@ packages:
|
|||
resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
deep-eql@5.0.2:
|
||||
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
deep-extend@0.6.0:
|
||||
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
|
|
@ -4796,9 +4751,6 @@ packages:
|
|||
es-module-lexer@0.9.3:
|
||||
resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==}
|
||||
|
||||
es-module-lexer@1.6.0:
|
||||
resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==}
|
||||
|
||||
es-object-atoms@1.1.1:
|
||||
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
|
@ -5045,9 +4997,6 @@ packages:
|
|||
estree-walker@2.0.2:
|
||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||
|
||||
esutils@2.0.3:
|
||||
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
|
@ -5103,10 +5052,6 @@ packages:
|
|||
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
expect-type@1.2.0:
|
||||
resolution: {integrity: sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
exponential-backoff@3.1.2:
|
||||
resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==}
|
||||
|
||||
|
|
@ -6403,9 +6348,6 @@ packages:
|
|||
loupe@2.3.7:
|
||||
resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
|
||||
|
||||
loupe@3.1.3:
|
||||
resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==}
|
||||
|
||||
lower-case@2.0.2:
|
||||
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
|
||||
|
||||
|
|
@ -6487,8 +6429,8 @@ packages:
|
|||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
mc-assets@0.2.59:
|
||||
resolution: {integrity: sha512-HGdy6v09X5nks8+NuwrL3KQ763D+eWFeSpWLXx3+doWz6hSEeLjgBPOrB1stvQOjPDiQCzsIv5gaRB5sl6ng1A==}
|
||||
mc-assets@0.2.62:
|
||||
resolution: {integrity: sha512-RYZeD1+joNlPuUpi+tIWkbP0ieVJr+R6IFkI6/8juhSxx9zE4osoSmteybrfspGm8A6u+YbbY1epqRKEMwVR6Q==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
mcraft-fun-mineflayer@0.1.23:
|
||||
|
|
@ -6699,19 +6641,19 @@ packages:
|
|||
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
minecraft-data@3.89.0:
|
||||
resolution: {integrity: sha512-v6dUr1M7Pjc6N4ujanrBZu3IP4/HbSBpxSSXNbK6HVFVJqfaqKSMXN57G/JAlDcwqXYsVd9H4tbKFHCO+VmQpg==}
|
||||
minecraft-data@3.92.0:
|
||||
resolution: {integrity: sha512-CGfO50svzm+pSRa4Mbq4owsmRKbPCNkSZ3MCOyH+epC7yNjh+PUhPQFHWq72O51qsY7pAB5qM/bJn1ncwG1J5g==}
|
||||
|
||||
minecraft-folder-path@1.2.0:
|
||||
resolution: {integrity: sha512-qaUSbKWoOsH9brn0JQuBhxNAzTDMwrOXorwuRxdJKKKDYvZhtml+6GVCUrY5HRiEsieBEjCUnhVpDuQiKsiFaw==}
|
||||
|
||||
minecraft-inventory-gui@https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/5494f356b1e59eddc876c3ae05ff395f12a46379:
|
||||
resolution: {tarball: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/5494f356b1e59eddc876c3ae05ff395f12a46379}
|
||||
minecraft-inventory-gui@https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/89c33d396f3fde4804c71f4be3c203ade1833b41:
|
||||
resolution: {tarball: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/89c33d396f3fde4804c71f4be3c203ade1833b41}
|
||||
version: 1.0.1
|
||||
|
||||
minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/9e116c3dd4682b17c4e2c80249a2447a093d9284:
|
||||
resolution: {tarball: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/9e116c3dd4682b17c4e2c80249a2447a093d9284}
|
||||
version: 1.57.0
|
||||
minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/6c2204a813690ead420e2b8c7f0ef32ca357d176:
|
||||
resolution: {tarball: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/6c2204a813690ead420e2b8c7f0ef32ca357d176}
|
||||
version: 1.58.0
|
||||
engines: {node: '>=22'}
|
||||
|
||||
minecraft-wrap@1.6.0:
|
||||
|
|
@ -6725,20 +6667,20 @@ packages:
|
|||
resolution: {tarball: https://codeload.github.com/zardoy/mineflayer-item-map-downloader/tar.gz/a8d210ecdcf78dd082fa149a96e1612cc9747824}
|
||||
version: 1.2.0
|
||||
|
||||
mineflayer-mouse@0.1.10:
|
||||
resolution: {integrity: sha512-bxrBzOVQX2Ok5KOiJMaoOLYaHMDecMkJpLn5oGIdyLRqiTF3WAnZc9oYQq+/f3YNA88W1vBIjai3v3vZe6wyaQ==}
|
||||
mineflayer-mouse@0.1.11:
|
||||
resolution: {integrity: sha512-BL47pXZ1+92BA/7ym6KaJctEHKnL0up+tpuagVwSKJvAgibeqWQJJwDlNUWkOLvpnruRKDxMR5OB1hUXFoDNSg==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
mineflayer-pathfinder@2.4.5:
|
||||
resolution: {integrity: sha512-Jh3JnUgRLwhMh2Dugo4SPza68C41y+NPP5sdsgxRu35ydndo70i1JJGxauVWbXrpNwIxYNztUw78aFyb7icw8g==}
|
||||
|
||||
mineflayer@4.27.0:
|
||||
resolution: {integrity: sha512-3bxph4jfbkBh5HpeouorxzrfSLNV+i+1gugNJ2jf52HW+rt+tW7eiiFPxrJEsOVkPT/3O/dEIW7j93LRlojMkQ==}
|
||||
mineflayer@4.30.0:
|
||||
resolution: {integrity: sha512-GtW4hkijyZbSu5LKYYD89xZu+XY7OoP7IkrCnNEn6EdPm0+vr2THoJgFGKrlze9/81+T+P3E4qvJXNFiU/zeJg==}
|
||||
engines: {node: '>=22'}
|
||||
|
||||
mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/0380bed150fe03db4ac37f0194e0cee98356647d:
|
||||
resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/0380bed150fe03db4ac37f0194e0cee98356647d}
|
||||
version: 4.27.0
|
||||
mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/3daf1f4bdc6afad0dedd87b879875f3dbb7b0980:
|
||||
resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/3daf1f4bdc6afad0dedd87b879875f3dbb7b0980}
|
||||
version: 4.30.0
|
||||
engines: {node: '>=22'}
|
||||
|
||||
minimalistic-assert@1.0.1:
|
||||
|
|
@ -7271,10 +7213,6 @@ packages:
|
|||
pathval@1.1.1:
|
||||
resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
|
||||
|
||||
pathval@2.0.0:
|
||||
resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
|
||||
engines: {node: '>= 14.16'}
|
||||
|
||||
pause-stream@0.0.11:
|
||||
resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==}
|
||||
|
||||
|
|
@ -7436,7 +7374,7 @@ packages:
|
|||
prismarine-biome@1.3.0:
|
||||
resolution: {integrity: sha512-GY6nZxq93mTErT7jD7jt8YS1aPrOakbJHh39seYsJFXvueIOdHAmW16kYQVrTVMW5MlWLQVxV/EquRwOgr4MnQ==}
|
||||
peerDependencies:
|
||||
minecraft-data: 3.89.0
|
||||
minecraft-data: 3.92.0
|
||||
prismarine-registry: ^1.1.0
|
||||
|
||||
prismarine-block@https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9:
|
||||
|
|
@ -8716,22 +8654,10 @@ packages:
|
|||
resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
tinypool@1.0.2:
|
||||
resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
|
||||
tinyrainbow@2.0.0:
|
||||
resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
tinyspy@2.2.1:
|
||||
resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
tinyspy@3.0.2:
|
||||
resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
title-case@3.0.3:
|
||||
resolution: {integrity: sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==}
|
||||
|
||||
|
|
@ -9218,11 +9144,6 @@ packages:
|
|||
engines: {node: '>=v14.18.0'}
|
||||
hasBin: true
|
||||
|
||||
vite-node@3.0.8:
|
||||
resolution: {integrity: sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg==}
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
hasBin: true
|
||||
|
||||
vite@4.5.9:
|
||||
resolution: {integrity: sha512-qK9W4xjgD3gXbC0NmdNFFnVFLMWSNiR3swj957yutwzzN16xF/E7nmtAyp1rT9hviDroQANjE4HK3H4WqWdFtw==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
|
|
@ -9322,34 +9243,6 @@ packages:
|
|||
webdriverio:
|
||||
optional: true
|
||||
|
||||
vitest@3.0.8:
|
||||
resolution: {integrity: sha512-dfqAsNqRGUc8hB9OVR2P0w8PZPEckti2+5rdZip0WIz9WW0MnImJ8XiR61QhqLa92EQzKP2uPkzenKOAHyEIbA==}
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@edge-runtime/vm': '*'
|
||||
'@types/debug': ^4.1.12
|
||||
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
|
||||
'@vitest/browser': 3.0.8
|
||||
'@vitest/ui': 3.0.8
|
||||
happy-dom: '*'
|
||||
jsdom: '*'
|
||||
peerDependenciesMeta:
|
||||
'@edge-runtime/vm':
|
||||
optional: true
|
||||
'@types/debug':
|
||||
optional: true
|
||||
'@types/node':
|
||||
optional: true
|
||||
'@vitest/browser':
|
||||
optional: true
|
||||
'@vitest/ui':
|
||||
optional: true
|
||||
happy-dom:
|
||||
optional: true
|
||||
jsdom:
|
||||
optional: true
|
||||
|
||||
vm-browserify@1.1.2:
|
||||
resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==}
|
||||
|
||||
|
|
@ -11422,8 +11315,8 @@ snapshots:
|
|||
'@nxg-org/mineflayer-trajectories@1.2.0(encoding@0.1.13)':
|
||||
dependencies:
|
||||
'@nxg-org/mineflayer-util-plugin': 1.8.4
|
||||
minecraft-data: 3.89.0
|
||||
mineflayer: 4.27.0(encoding@0.1.13)
|
||||
minecraft-data: 3.92.0
|
||||
mineflayer: 4.30.0(encoding@0.1.13)
|
||||
prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9
|
||||
prismarine-item: 1.16.0
|
||||
prismarine-physics: https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b
|
||||
|
|
@ -13086,68 +12979,28 @@ snapshots:
|
|||
'@vitest/utils': 0.34.6
|
||||
chai: 4.5.0
|
||||
|
||||
'@vitest/expect@3.0.8':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.0.8
|
||||
'@vitest/utils': 3.0.8
|
||||
chai: 5.2.0
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/mocker@3.0.8(vite@6.2.1(@types/node@22.13.9)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.0.8
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.17
|
||||
optionalDependencies:
|
||||
vite: 6.2.1(@types/node@22.13.9)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)
|
||||
|
||||
'@vitest/pretty-format@3.0.8':
|
||||
dependencies:
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/runner@0.34.6':
|
||||
dependencies:
|
||||
'@vitest/utils': 0.34.6
|
||||
p-limit: 4.0.0
|
||||
pathe: 1.1.2
|
||||
|
||||
'@vitest/runner@3.0.8':
|
||||
dependencies:
|
||||
'@vitest/utils': 3.0.8
|
||||
pathe: 2.0.3
|
||||
|
||||
'@vitest/snapshot@0.34.6':
|
||||
dependencies:
|
||||
magic-string: 0.30.17
|
||||
pathe: 1.1.2
|
||||
pretty-format: 29.7.0
|
||||
|
||||
'@vitest/snapshot@3.0.8':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 3.0.8
|
||||
magic-string: 0.30.17
|
||||
pathe: 2.0.3
|
||||
|
||||
'@vitest/spy@0.34.6':
|
||||
dependencies:
|
||||
tinyspy: 2.2.1
|
||||
|
||||
'@vitest/spy@3.0.8':
|
||||
dependencies:
|
||||
tinyspy: 3.0.2
|
||||
|
||||
'@vitest/utils@0.34.6':
|
||||
dependencies:
|
||||
diff-sequences: 29.6.3
|
||||
loupe: 2.3.7
|
||||
pretty-format: 29.7.0
|
||||
|
||||
'@vitest/utils@3.0.8':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 3.0.8
|
||||
loupe: 3.1.3
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@xboxreplay/errors@0.1.0': {}
|
||||
|
||||
'@xboxreplay/xboxlive-auth@3.3.3(debug@4.4.0)':
|
||||
|
|
@ -13223,16 +13076,16 @@ snapshots:
|
|||
exit-hook: 2.2.1
|
||||
flatmap: 0.0.3
|
||||
long: 5.3.1
|
||||
minecraft-data: 3.89.0
|
||||
minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/9e116c3dd4682b17c4e2c80249a2447a093d9284(patch_hash=09def7a73311f10b6aa8ff9f9b76129589578fa154a8b846b06909ca748c4762)(encoding@0.1.13)
|
||||
minecraft-data: 3.92.0
|
||||
minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/6c2204a813690ead420e2b8c7f0ef32ca357d176(patch_hash=a8726e6981ddc3486262d981d1e2030f379901c055ac9c4bf3036b4149e860e0)(encoding@0.1.13)
|
||||
mkdirp: 2.1.6
|
||||
node-gzip: 1.1.2
|
||||
node-rsa: 1.1.1
|
||||
prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/c5feac83b61d95feb4d4f22c063dacfb8c192a9f(minecraft-data@3.89.0)
|
||||
prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/c5feac83b61d95feb4d4f22c063dacfb8c192a9f(minecraft-data@3.92.0)
|
||||
prismarine-entity: 2.5.0
|
||||
prismarine-item: 1.16.0
|
||||
prismarine-nbt: 2.7.0
|
||||
prismarine-provider-anvil: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/1d548fac63fe977c8281f0a9a522b37e4d92d0b7(minecraft-data@3.89.0)
|
||||
prismarine-provider-anvil: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/1d548fac63fe977c8281f0a9a522b37e4d92d0b7(minecraft-data@3.92.0)
|
||||
prismarine-windows: 2.9.0
|
||||
prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/ab2146c9933eef3247c3f64446de4ccc2c484c7c
|
||||
rambda: 9.4.2
|
||||
|
|
@ -13259,16 +13112,16 @@ snapshots:
|
|||
exit-hook: 2.2.1
|
||||
flatmap: 0.0.3
|
||||
long: 5.3.1
|
||||
minecraft-data: 3.89.0
|
||||
minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/9e116c3dd4682b17c4e2c80249a2447a093d9284(patch_hash=09def7a73311f10b6aa8ff9f9b76129589578fa154a8b846b06909ca748c4762)(encoding@0.1.13)
|
||||
minecraft-data: 3.92.0
|
||||
minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/6c2204a813690ead420e2b8c7f0ef32ca357d176(patch_hash=a8726e6981ddc3486262d981d1e2030f379901c055ac9c4bf3036b4149e860e0)(encoding@0.1.13)
|
||||
mkdirp: 2.1.6
|
||||
node-gzip: 1.1.2
|
||||
node-rsa: 1.1.1
|
||||
prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/c5feac83b61d95feb4d4f22c063dacfb8c192a9f(minecraft-data@3.89.0)
|
||||
prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/c5feac83b61d95feb4d4f22c063dacfb8c192a9f(minecraft-data@3.92.0)
|
||||
prismarine-entity: 2.5.0
|
||||
prismarine-item: 1.16.0
|
||||
prismarine-nbt: 2.7.0
|
||||
prismarine-provider-anvil: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/1d548fac63fe977c8281f0a9a522b37e4d92d0b7(minecraft-data@3.89.0)
|
||||
prismarine-provider-anvil: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/1d548fac63fe977c8281f0a9a522b37e4d92d0b7(minecraft-data@3.92.0)
|
||||
prismarine-windows: 2.9.0
|
||||
prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/ab2146c9933eef3247c3f64446de4ccc2c484c7c
|
||||
rambda: 9.4.2
|
||||
|
|
@ -13562,8 +13415,6 @@ snapshots:
|
|||
|
||||
assertion-error@1.1.0: {}
|
||||
|
||||
assertion-error@2.0.1: {}
|
||||
|
||||
assign-symbols@1.0.0: {}
|
||||
|
||||
ast-types@0.16.1:
|
||||
|
|
@ -13995,14 +13846,6 @@ snapshots:
|
|||
pathval: 1.1.1
|
||||
type-detect: 4.1.0
|
||||
|
||||
chai@5.2.0:
|
||||
dependencies:
|
||||
assertion-error: 2.0.1
|
||||
check-error: 2.1.1
|
||||
deep-eql: 5.0.2
|
||||
loupe: 3.1.3
|
||||
pathval: 2.0.0
|
||||
|
||||
chalk@2.4.2:
|
||||
dependencies:
|
||||
ansi-styles: 3.2.1
|
||||
|
|
@ -14041,8 +13884,6 @@ snapshots:
|
|||
dependencies:
|
||||
get-func-name: 2.0.2
|
||||
|
||||
check-error@2.1.1: {}
|
||||
|
||||
check-more-types@2.24.0:
|
||||
optional: true
|
||||
|
||||
|
|
@ -14557,8 +14398,6 @@ snapshots:
|
|||
dependencies:
|
||||
type-detect: 4.1.0
|
||||
|
||||
deep-eql@5.0.2: {}
|
||||
|
||||
deep-extend@0.6.0: {}
|
||||
|
||||
deep-is@0.1.4: {}
|
||||
|
|
@ -14673,8 +14512,8 @@ snapshots:
|
|||
|
||||
diamond-square@https://codeload.github.com/zardoy/diamond-square/tar.gz/cfaad2d1d5909fdfa63c8cc7bc05fb5e87782d71:
|
||||
dependencies:
|
||||
minecraft-data: 3.89.0
|
||||
prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/c5feac83b61d95feb4d4f22c063dacfb8c192a9f(minecraft-data@3.89.0)
|
||||
minecraft-data: 3.92.0
|
||||
prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/c5feac83b61d95feb4d4f22c063dacfb8c192a9f(minecraft-data@3.92.0)
|
||||
prismarine-registry: 1.11.0
|
||||
random-seed: 0.3.0
|
||||
vec3: 0.1.10
|
||||
|
|
@ -15056,8 +14895,6 @@ snapshots:
|
|||
|
||||
es-module-lexer@0.9.3: {}
|
||||
|
||||
es-module-lexer@1.6.0: {}
|
||||
|
||||
es-object-atoms@1.1.1:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
|
|
@ -15440,10 +15277,6 @@ snapshots:
|
|||
|
||||
estree-walker@2.0.2: {}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.6
|
||||
|
||||
esutils@2.0.3: {}
|
||||
|
||||
etag@1.8.1: {}
|
||||
|
|
@ -15521,8 +15354,6 @@ snapshots:
|
|||
|
||||
expand-template@2.0.3: {}
|
||||
|
||||
expect-type@1.2.0: {}
|
||||
|
||||
exponential-backoff@3.1.2:
|
||||
optional: true
|
||||
|
||||
|
|
@ -17024,8 +16855,6 @@ snapshots:
|
|||
dependencies:
|
||||
get-func-name: 2.0.2
|
||||
|
||||
loupe@3.1.3: {}
|
||||
|
||||
lower-case@2.0.2:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
|
@ -17122,17 +16951,17 @@ snapshots:
|
|||
|
||||
math-intrinsics@1.1.0: {}
|
||||
|
||||
mc-assets@0.2.59:
|
||||
mc-assets@0.2.62:
|
||||
dependencies:
|
||||
maxrects-packer: '@zardoy/maxrects-packer@2.7.4'
|
||||
zod: 3.24.2
|
||||
|
||||
mcraft-fun-mineflayer@0.1.23(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/0380bed150fe03db4ac37f0194e0cee98356647d(encoding@0.1.13)):
|
||||
mcraft-fun-mineflayer@0.1.23(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/3daf1f4bdc6afad0dedd87b879875f3dbb7b0980(encoding@0.1.13)):
|
||||
dependencies:
|
||||
'@zardoy/flying-squid': 0.0.49(encoding@0.1.13)
|
||||
exit-hook: 2.2.1
|
||||
minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/9e116c3dd4682b17c4e2c80249a2447a093d9284(patch_hash=09def7a73311f10b6aa8ff9f9b76129589578fa154a8b846b06909ca748c4762)(encoding@0.1.13)
|
||||
mineflayer: https://codeload.github.com/zardoy/mineflayer/tar.gz/0380bed150fe03db4ac37f0194e0cee98356647d(encoding@0.1.13)
|
||||
minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/6c2204a813690ead420e2b8c7f0ef32ca357d176(patch_hash=a8726e6981ddc3486262d981d1e2030f379901c055ac9c4bf3036b4149e860e0)(encoding@0.1.13)
|
||||
mineflayer: https://codeload.github.com/zardoy/mineflayer/tar.gz/3daf1f4bdc6afad0dedd87b879875f3dbb7b0980(encoding@0.1.13)
|
||||
prismarine-item: 1.16.0
|
||||
ws: 8.18.1
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -17439,18 +17268,18 @@ snapshots:
|
|||
|
||||
min-indent@1.0.1: {}
|
||||
|
||||
minecraft-data@3.89.0: {}
|
||||
minecraft-data@3.92.0: {}
|
||||
|
||||
minecraft-folder-path@1.2.0: {}
|
||||
|
||||
minecraft-inventory-gui@https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/5494f356b1e59eddc876c3ae05ff395f12a46379(@types/react@18.3.18)(react@18.3.1):
|
||||
minecraft-inventory-gui@https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/89c33d396f3fde4804c71f4be3c203ade1833b41(@types/react@18.3.18)(react@18.3.1):
|
||||
dependencies:
|
||||
valtio: 1.13.2(@types/react@18.3.18)(react@18.3.1)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- react
|
||||
|
||||
minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/9e116c3dd4682b17c4e2c80249a2447a093d9284(patch_hash=09def7a73311f10b6aa8ff9f9b76129589578fa154a8b846b06909ca748c4762)(encoding@0.1.13):
|
||||
minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/6c2204a813690ead420e2b8c7f0ef32ca357d176(patch_hash=a8726e6981ddc3486262d981d1e2030f379901c055ac9c4bf3036b4149e860e0)(encoding@0.1.13):
|
||||
dependencies:
|
||||
'@types/node-rsa': 1.1.4
|
||||
'@types/readable-stream': 4.0.18
|
||||
|
|
@ -17459,7 +17288,7 @@ snapshots:
|
|||
debug: 4.4.0(supports-color@8.1.1)
|
||||
endian-toggle: 0.0.0
|
||||
lodash.merge: 4.6.2
|
||||
minecraft-data: 3.89.0
|
||||
minecraft-data: 3.92.0
|
||||
minecraft-folder-path: 1.2.0
|
||||
node-fetch: 2.7.0(encoding@0.1.13)
|
||||
node-rsa: 0.4.2
|
||||
|
|
@ -17502,43 +17331,24 @@ snapshots:
|
|||
|
||||
mineflayer-item-map-downloader@https://codeload.github.com/zardoy/mineflayer-item-map-downloader/tar.gz/a8d210ecdcf78dd082fa149a96e1612cc9747824(patch_hash=a731ebbace2d8790c973ab3a5ba33494a6e9658533a9710dd8ba36f86db061ad)(encoding@0.1.13):
|
||||
dependencies:
|
||||
mineflayer: 4.27.0(encoding@0.1.13)
|
||||
mineflayer: 4.30.0(encoding@0.1.13)
|
||||
sharp: 0.30.7
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
mineflayer-mouse@0.1.10(@types/debug@4.1.12)(@types/node@22.13.9)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0):
|
||||
mineflayer-mouse@0.1.11:
|
||||
dependencies:
|
||||
change-case: 5.4.4
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.1
|
||||
prismarine-item: 1.16.0
|
||||
prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/ab2146c9933eef3247c3f64446de4ccc2c484c7c
|
||||
vitest: 3.0.8(@types/debug@4.1.12)(@types/node@22.13.9)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)
|
||||
transitivePeerDependencies:
|
||||
- '@edge-runtime/vm'
|
||||
- '@types/debug'
|
||||
- '@types/node'
|
||||
- '@vitest/browser'
|
||||
- '@vitest/ui'
|
||||
- happy-dom
|
||||
- jiti
|
||||
- jsdom
|
||||
- less
|
||||
- lightningcss
|
||||
- msw
|
||||
- sass
|
||||
- sass-embedded
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
- terser
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
mineflayer-pathfinder@2.4.5:
|
||||
dependencies:
|
||||
minecraft-data: 3.89.0
|
||||
minecraft-data: 3.92.0
|
||||
prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9
|
||||
prismarine-entity: 2.5.0
|
||||
prismarine-item: 1.16.0
|
||||
|
|
@ -17546,14 +17356,14 @@ snapshots:
|
|||
prismarine-physics: https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b
|
||||
vec3: 0.1.10
|
||||
|
||||
mineflayer@4.27.0(encoding@0.1.13):
|
||||
mineflayer@4.30.0(encoding@0.1.13):
|
||||
dependencies:
|
||||
minecraft-data: 3.89.0
|
||||
minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/9e116c3dd4682b17c4e2c80249a2447a093d9284(patch_hash=09def7a73311f10b6aa8ff9f9b76129589578fa154a8b846b06909ca748c4762)(encoding@0.1.13)
|
||||
prismarine-biome: 1.3.0(minecraft-data@3.89.0)(prismarine-registry@1.11.0)
|
||||
minecraft-data: 3.92.0
|
||||
minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/6c2204a813690ead420e2b8c7f0ef32ca357d176(patch_hash=a8726e6981ddc3486262d981d1e2030f379901c055ac9c4bf3036b4149e860e0)(encoding@0.1.13)
|
||||
prismarine-biome: 1.3.0(minecraft-data@3.92.0)(prismarine-registry@1.11.0)
|
||||
prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9
|
||||
prismarine-chat: 1.11.0
|
||||
prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/c5feac83b61d95feb4d4f22c063dacfb8c192a9f(minecraft-data@3.89.0)
|
||||
prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/c5feac83b61d95feb4d4f22c063dacfb8c192a9f(minecraft-data@3.92.0)
|
||||
prismarine-entity: 2.5.0
|
||||
prismarine-item: 1.16.0
|
||||
prismarine-nbt: 2.7.0
|
||||
|
|
@ -17569,15 +17379,15 @@ snapshots:
|
|||
- encoding
|
||||
- supports-color
|
||||
|
||||
mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/0380bed150fe03db4ac37f0194e0cee98356647d(encoding@0.1.13):
|
||||
mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/3daf1f4bdc6afad0dedd87b879875f3dbb7b0980(encoding@0.1.13):
|
||||
dependencies:
|
||||
'@nxg-org/mineflayer-physics-util': 1.8.10
|
||||
minecraft-data: 3.89.0
|
||||
minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/9e116c3dd4682b17c4e2c80249a2447a093d9284(patch_hash=09def7a73311f10b6aa8ff9f9b76129589578fa154a8b846b06909ca748c4762)(encoding@0.1.13)
|
||||
prismarine-biome: 1.3.0(minecraft-data@3.89.0)(prismarine-registry@1.11.0)
|
||||
minecraft-data: 3.92.0
|
||||
minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/6c2204a813690ead420e2b8c7f0ef32ca357d176(patch_hash=a8726e6981ddc3486262d981d1e2030f379901c055ac9c4bf3036b4149e860e0)(encoding@0.1.13)
|
||||
prismarine-biome: 1.3.0(minecraft-data@3.92.0)(prismarine-registry@1.11.0)
|
||||
prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9
|
||||
prismarine-chat: 1.11.0
|
||||
prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/c5feac83b61d95feb4d4f22c063dacfb8c192a9f(minecraft-data@3.89.0)
|
||||
prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/c5feac83b61d95feb4d4f22c063dacfb8c192a9f(minecraft-data@3.92.0)
|
||||
prismarine-entity: 2.5.0
|
||||
prismarine-item: 1.16.0
|
||||
prismarine-nbt: 2.7.0
|
||||
|
|
@ -18202,8 +18012,6 @@ snapshots:
|
|||
|
||||
pathval@1.1.1: {}
|
||||
|
||||
pathval@2.0.0: {}
|
||||
|
||||
pause-stream@0.0.11:
|
||||
dependencies:
|
||||
through: 2.3.8
|
||||
|
|
@ -18263,7 +18071,7 @@ snapshots:
|
|||
|
||||
pirates@4.0.6: {}
|
||||
|
||||
pixelarticons@1.8.1(patch_hash=d6a3d784047beba873565d1198bed425d9eb2de942e3fc8edac55f25473e4325): {}
|
||||
pixelarticons@1.8.1(patch_hash=533230072bc402f425c86abd3d0356fe087b14cab2a254d93f419b083f2d8dfa): {}
|
||||
|
||||
pixelmatch@4.0.2:
|
||||
dependencies:
|
||||
|
|
@ -18363,15 +18171,15 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
prismarine-biome@1.3.0(minecraft-data@3.89.0)(prismarine-registry@1.11.0):
|
||||
prismarine-biome@1.3.0(minecraft-data@3.92.0)(prismarine-registry@1.11.0):
|
||||
dependencies:
|
||||
minecraft-data: 3.89.0
|
||||
minecraft-data: 3.92.0
|
||||
prismarine-registry: 1.11.0
|
||||
|
||||
prismarine-block@https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9:
|
||||
dependencies:
|
||||
minecraft-data: 3.89.0
|
||||
prismarine-biome: 1.3.0(minecraft-data@3.89.0)(prismarine-registry@1.11.0)
|
||||
minecraft-data: 3.92.0
|
||||
prismarine-biome: 1.3.0(minecraft-data@3.92.0)(prismarine-registry@1.11.0)
|
||||
prismarine-chat: 1.11.0
|
||||
prismarine-item: 1.16.0
|
||||
prismarine-nbt: 2.7.0
|
||||
|
|
@ -18383,9 +18191,9 @@ snapshots:
|
|||
prismarine-nbt: 2.7.0
|
||||
prismarine-registry: 1.11.0
|
||||
|
||||
prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/c5feac83b61d95feb4d4f22c063dacfb8c192a9f(minecraft-data@3.89.0):
|
||||
prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/c5feac83b61d95feb4d4f22c063dacfb8c192a9f(minecraft-data@3.92.0):
|
||||
dependencies:
|
||||
prismarine-biome: 1.3.0(minecraft-data@3.89.0)(prismarine-registry@1.11.0)
|
||||
prismarine-biome: 1.3.0(minecraft-data@3.92.0)(prismarine-registry@1.11.0)
|
||||
prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9
|
||||
prismarine-nbt: 2.7.0
|
||||
prismarine-registry: 1.11.0
|
||||
|
|
@ -18414,14 +18222,14 @@ snapshots:
|
|||
|
||||
prismarine-physics@https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b:
|
||||
dependencies:
|
||||
minecraft-data: 3.89.0
|
||||
minecraft-data: 3.92.0
|
||||
prismarine-nbt: 2.7.0
|
||||
vec3: 0.1.10
|
||||
|
||||
prismarine-provider-anvil@https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/1d548fac63fe977c8281f0a9a522b37e4d92d0b7(minecraft-data@3.89.0):
|
||||
prismarine-provider-anvil@https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/1d548fac63fe977c8281f0a9a522b37e4d92d0b7(minecraft-data@3.92.0):
|
||||
dependencies:
|
||||
prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9
|
||||
prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/c5feac83b61d95feb4d4f22c063dacfb8c192a9f(minecraft-data@3.89.0)
|
||||
prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/c5feac83b61d95feb4d4f22c063dacfb8c192a9f(minecraft-data@3.92.0)
|
||||
prismarine-nbt: 2.7.0
|
||||
prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/ab2146c9933eef3247c3f64446de4ccc2c484c7c
|
||||
uint4: 0.1.2
|
||||
|
|
@ -18443,13 +18251,13 @@ snapshots:
|
|||
|
||||
prismarine-registry@1.11.0:
|
||||
dependencies:
|
||||
minecraft-data: 3.89.0
|
||||
minecraft-data: 3.92.0
|
||||
prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9
|
||||
prismarine-nbt: 2.7.0
|
||||
|
||||
prismarine-schematic@1.2.3:
|
||||
dependencies:
|
||||
minecraft-data: 3.89.0
|
||||
minecraft-data: 3.92.0
|
||||
prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9
|
||||
prismarine-nbt: 2.7.0
|
||||
prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/ab2146c9933eef3247c3f64446de4ccc2c484c7c
|
||||
|
|
@ -20064,14 +19872,8 @@ snapshots:
|
|||
|
||||
tinypool@0.7.0: {}
|
||||
|
||||
tinypool@1.0.2: {}
|
||||
|
||||
tinyrainbow@2.0.0: {}
|
||||
|
||||
tinyspy@2.2.1: {}
|
||||
|
||||
tinyspy@3.0.2: {}
|
||||
|
||||
title-case@3.0.3:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
|
@ -20560,27 +20362,6 @@ snapshots:
|
|||
- supports-color
|
||||
- terser
|
||||
|
||||
vite-node@3.0.8(@types/node@22.13.9)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.4.1
|
||||
es-module-lexer: 1.6.0
|
||||
pathe: 2.0.3
|
||||
vite: 6.2.1(@types/node@22.13.9)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- jiti
|
||||
- less
|
||||
- lightningcss
|
||||
- sass
|
||||
- sass-embedded
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
- terser
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vite@4.5.9(@types/node@22.13.9)(terser@5.39.0):
|
||||
dependencies:
|
||||
esbuild: 0.18.20
|
||||
|
|
@ -20639,45 +20420,6 @@ snapshots:
|
|||
- supports-color
|
||||
- terser
|
||||
|
||||
vitest@3.0.8(@types/debug@4.1.12)(@types/node@22.13.9)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0):
|
||||
dependencies:
|
||||
'@vitest/expect': 3.0.8
|
||||
'@vitest/mocker': 3.0.8(vite@6.2.1(@types/node@22.13.9)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))
|
||||
'@vitest/pretty-format': 3.0.8
|
||||
'@vitest/runner': 3.0.8
|
||||
'@vitest/snapshot': 3.0.8
|
||||
'@vitest/spy': 3.0.8
|
||||
'@vitest/utils': 3.0.8
|
||||
chai: 5.2.0
|
||||
debug: 4.4.1
|
||||
expect-type: 1.2.0
|
||||
magic-string: 0.30.17
|
||||
pathe: 2.0.3
|
||||
std-env: 3.8.1
|
||||
tinybench: 2.9.0
|
||||
tinyexec: 0.3.2
|
||||
tinypool: 1.0.2
|
||||
tinyrainbow: 2.0.0
|
||||
vite: 6.2.1(@types/node@22.13.9)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)
|
||||
vite-node: 3.0.8(@types/node@22.13.9)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/debug': 4.1.12
|
||||
'@types/node': 22.13.9
|
||||
transitivePeerDependencies:
|
||||
- jiti
|
||||
- less
|
||||
- lightningcss
|
||||
- msw
|
||||
- sass
|
||||
- sass-embedded
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
- terser
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vm-browserify@1.1.2: {}
|
||||
|
||||
w3c-keyname@2.2.8: {}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { ItemSelector } from 'mc-assets/dist/itemDefinitions'
|
||||
import { GameMode } from 'mineflayer'
|
||||
import { GameMode, Team } from 'mineflayer'
|
||||
import { proxy } from 'valtio'
|
||||
import type { HandItemBlock } from '../three/holdingBlock'
|
||||
|
||||
|
|
@ -50,6 +50,8 @@ export const getInitialPlayerState = () => proxy({
|
|||
perspective: 'first_person' as CameraPerspective,
|
||||
|
||||
cameraSpectatingEntity: undefined as number | undefined,
|
||||
|
||||
team: undefined as Team | undefined,
|
||||
})
|
||||
|
||||
export const getPlayerStateUtils = (reactive: PlayerStateReactive) => ({
|
||||
|
|
|
|||
|
|
@ -765,7 +765,6 @@ export function getSectionGeometry (sx: number, sy: number, sz: number, world: W
|
|||
heads: {},
|
||||
signs: {},
|
||||
// isFull: true,
|
||||
highestBlocks: new Map(),
|
||||
hadErrors: false,
|
||||
blocksCount: 0,
|
||||
instancedBlocks: {}
|
||||
|
|
@ -776,12 +775,6 @@ export function getSectionGeometry (sx: number, sy: number, sz: number, world: W
|
|||
for (cursor.z = sz; cursor.z < sz + 16; cursor.z++) {
|
||||
for (cursor.x = sx; cursor.x < sx + 16; cursor.x++) {
|
||||
let block = world.getBlock(cursor, blockProvider, attr)!
|
||||
if (!INVISIBLE_BLOCKS.has(block.name)) {
|
||||
const highest = attr.highestBlocks.get(`${cursor.x},${cursor.z}`)
|
||||
if (!highest || highest.y < cursor.y) {
|
||||
attr.highestBlocks.set(`${cursor.x},${cursor.z}`, { y: cursor.y, stateId: block.stateId, biomeId: block.biome.id })
|
||||
}
|
||||
}
|
||||
if (INVISIBLE_BLOCKS.has(block.name)) continue
|
||||
if ((block.name.includes('_sign') || block.name === 'sign') && !world.config.disableSignsMapsSupport) {
|
||||
const key = `${cursor.x},${cursor.y},${cursor.z}`
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ export type MesherGeometryOutput = {
|
|||
heads: Record<string, any>,
|
||||
signs: Record<string, any>,
|
||||
// isFull: boolean
|
||||
highestBlocks: Map<string, HighestBlockInfo>
|
||||
hadErrors: boolean
|
||||
blocksCount: number
|
||||
customBlockModels?: CustomBlockModels
|
||||
|
|
|
|||
|
|
@ -3,6 +3,20 @@ import * as THREE from 'three'
|
|||
import stevePng from 'mc-assets/dist/other-textures/latest/entity/player/wide/steve.png'
|
||||
import { getLoadedImage } from 'mc-assets/dist/utils'
|
||||
|
||||
const detectFullOffscreenCanvasSupport = () => {
|
||||
if (typeof OffscreenCanvas === 'undefined') return false
|
||||
try {
|
||||
const canvas = new OffscreenCanvas(1, 1)
|
||||
// Try to get a WebGL context - this will fail on iOS where only 2D is supported (iOS 16)
|
||||
const gl = canvas.getContext('webgl2') || canvas.getContext('webgl')
|
||||
return gl !== null
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const hasFullOffscreenCanvasSupport = detectFullOffscreenCanvasSupport()
|
||||
|
||||
export const loadThreeJsTextureFromUrlSync = (imageUrl: string) => {
|
||||
const texture = new THREE.Texture()
|
||||
const promise = getLoadedImage(imageUrl).then(image => {
|
||||
|
|
@ -16,12 +30,22 @@ export const loadThreeJsTextureFromUrlSync = (imageUrl: string) => {
|
|||
}
|
||||
}
|
||||
|
||||
export const createCanvas = (width: number, height: number): OffscreenCanvas => {
|
||||
if (hasFullOffscreenCanvasSupport) {
|
||||
return new OffscreenCanvas(width, height)
|
||||
}
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = width
|
||||
canvas.height = height
|
||||
return canvas as unknown as OffscreenCanvas // todo-low
|
||||
}
|
||||
|
||||
export const loadThreeJsTextureFromUrl = async (imageUrl: string) => {
|
||||
const loaded = new THREE.TextureLoader().loadAsync(imageUrl)
|
||||
return loaded
|
||||
}
|
||||
export const loadThreeJsTextureFromBitmap = (image: ImageBitmap) => {
|
||||
const canvas = new OffscreenCanvas(image.width, image.height)
|
||||
const canvas = createCanvas(image.width, image.height)
|
||||
const ctx = canvas.getContext('2d')!
|
||||
ctx.drawImage(image, 0, 0)
|
||||
const texture = new THREE.Texture(canvas)
|
||||
|
|
@ -83,7 +107,7 @@ export async function loadSkinImage (skinUrl: string): Promise<{ canvas: Offscre
|
|||
}
|
||||
|
||||
const image = await loadImageFromUrl(skinUrl)
|
||||
const skinCanvas = new OffscreenCanvas(64, 64)
|
||||
const skinCanvas = createCanvas(64, 64)
|
||||
loadSkinToCanvas(skinCanvas, image)
|
||||
return { canvas: skinCanvas, image }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ export class WorldDataEmitter extends (EventEmitter as new () => TypedEmitter<Wo
|
|||
...e,
|
||||
pos: e.position,
|
||||
username: e.username,
|
||||
team: bot.teamMap[e.username] || bot.teamMap[e.uuid],
|
||||
// set debugTree (obj) {
|
||||
// e.debugTree = obj
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ export const defaultWorldRendererConfig = {
|
|||
mesherWorkers: 4,
|
||||
isPlayground: false,
|
||||
renderEars: true,
|
||||
skinTexturesProxy: undefined as string | undefined,
|
||||
// game renderer setting actually
|
||||
showHand: false,
|
||||
viewBobbing: false,
|
||||
|
|
@ -127,7 +128,6 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|||
|
||||
handleResize = () => { }
|
||||
highestBlocksByChunks = new Map<string, { [chunkKey: string]: HighestBlockInfo }>()
|
||||
highestBlocksBySections = new Map<string, { [sectionKey: string]: HighestBlockInfo }>()
|
||||
blockEntities = {}
|
||||
|
||||
workersProcessAverageTime = 0
|
||||
|
|
@ -393,8 +393,6 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|||
this.logWorkerWork(() => `-> ${data.workerIndex} geometry ${data.key} ${JSON.stringify({ dataSize: JSON.stringify(data).length })}`)
|
||||
this.geometryReceiveCount[data.workerIndex] ??= 0
|
||||
this.geometryReceiveCount[data.workerIndex]++
|
||||
const { geometry } = data
|
||||
this.highestBlocksBySections[data.key] = geometry.highestBlocks
|
||||
const chunkCoords = data.key.split(',').map(Number)
|
||||
this.lastChunkDistance = Math.max(...this.getDistance(new Vec3(chunkCoords[0], 0, chunkCoords[2])))
|
||||
}
|
||||
|
|
@ -692,7 +690,6 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|||
for (let y = this.worldSizeParams.minY; y < this.worldSizeParams.worldHeight; y += 16) {
|
||||
this.setSectionDirty(new Vec3(x, y, z), false)
|
||||
delete this.finishedSections[`${x},${y},${z}`]
|
||||
this.highestBlocksBySections.delete(`${x},${y},${z}`)
|
||||
}
|
||||
this.highestBlocksByChunks.delete(`${x},${z}`)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@ import { Item } from 'prismarine-item'
|
|||
import { BlockModel } from 'mc-assets'
|
||||
import { isEntityAttackable } from 'mineflayer-mouse/dist/attackableEntity'
|
||||
import { Vec3 } from 'vec3'
|
||||
import { Team } from 'mineflayer'
|
||||
import { EntityMetadataVersions } from '../../../src/mcDataTypes'
|
||||
import { ItemSpecificContextProperties } from '../lib/basePlayerState'
|
||||
import { loadSkinImage, loadSkinFromUsername, stevePngUrl, steveTexture } from '../lib/utils/skins'
|
||||
import { loadSkinImage, loadSkinFromUsername, stevePngUrl, steveTexture, createCanvas } from '../lib/utils/skins'
|
||||
import { loadTexture } from '../lib/utils'
|
||||
import { getBlockMeshFromModel } from './holdingBlock'
|
||||
import * as Entity from './entity/EntityMesh'
|
||||
|
|
@ -96,7 +97,7 @@ function getUsernameTexture ({
|
|||
nameTagBackgroundColor = 'rgba(0, 0, 0, 0.3)',
|
||||
nameTagTextOpacity = 255
|
||||
}: any, { fontFamily = 'sans-serif' }: any) {
|
||||
const canvas = new OffscreenCanvas(64, 64)
|
||||
const canvas = createCanvas(64, 64)
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (!ctx) throw new Error('Could not get 2d context')
|
||||
|
||||
|
|
@ -209,7 +210,7 @@ export type SceneEntity = THREE.Object3D & {
|
|||
username?: string
|
||||
uuid?: string
|
||||
additionalCleanup?: () => void
|
||||
originalEntity: import('prismarine-entity').Entity & { delete?; pos?, name }
|
||||
originalEntity: import('prismarine-entity').Entity & { delete?; pos?, name, team?: Team }
|
||||
}
|
||||
|
||||
export class Entities {
|
||||
|
|
@ -485,6 +486,7 @@ export class Entities {
|
|||
.some(channel => channel !== 0)
|
||||
}
|
||||
|
||||
// todo true/undefined doesnt reset the skin to the default one
|
||||
// eslint-disable-next-line max-params
|
||||
async updatePlayerSkin (entityId: string | number, username: string | undefined, uuidCache: string | undefined, skinUrl: string | true, capeUrl: string | true | undefined = undefined) {
|
||||
if (uuidCache) {
|
||||
|
|
@ -550,7 +552,7 @@ export class Entities {
|
|||
let skinCanvas: OffscreenCanvas
|
||||
if (skinUrl === stevePngUrl) {
|
||||
skinTexture = await steveTexture
|
||||
const canvas = new OffscreenCanvas(64, 64)
|
||||
const canvas = createCanvas(64, 64)
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (!ctx) throw new Error('Failed to get context')
|
||||
ctx.drawImage(skinTexture.image, 0, 0)
|
||||
|
|
@ -900,6 +902,8 @@ export class Entities {
|
|||
nameTag.position.y = playerObject.position.y + playerObject.scale.y * 16 + 3
|
||||
nameTag.renderOrder = 1000
|
||||
|
||||
nameTag.name = 'nametag'
|
||||
|
||||
//@ts-expect-error
|
||||
wrapper.add(nameTag)
|
||||
}
|
||||
|
|
@ -1111,10 +1115,12 @@ export class Entities {
|
|||
}
|
||||
}
|
||||
|
||||
if (entity.username) {
|
||||
if (entity.username !== undefined) {
|
||||
e.username = entity.username
|
||||
}
|
||||
|
||||
this.updateNameTagVisibility(e)
|
||||
|
||||
this.updateEntityPosition(entity, justAdded, overrides)
|
||||
}
|
||||
|
||||
|
|
@ -1183,6 +1189,20 @@ export class Entities {
|
|||
}
|
||||
}
|
||||
|
||||
updateNameTagVisibility (entity: SceneEntity) {
|
||||
const playerTeam = this.worldRenderer.playerStateReactive.team
|
||||
const entityTeam = entity.originalEntity.team
|
||||
const nameTagVisibility = entityTeam?.nameTagVisibility || 'always'
|
||||
const showNameTag = nameTagVisibility === 'always' ||
|
||||
(nameTagVisibility === 'hideForOwnTeam' && entityTeam?.team !== playerTeam?.team) ||
|
||||
(nameTagVisibility === 'hideForOtherTeams' && (entityTeam?.team === playerTeam?.team || playerTeam === undefined))
|
||||
entity.traverse(c => {
|
||||
if (c.name === 'nametag') {
|
||||
c.visible = showNameTag
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
addMapModel (entityMesh: THREE.Object3D, mapNumber: number, rotation: number) {
|
||||
const imageData = this.cachedMapsImages?.[mapNumber]
|
||||
let texture: THREE.Texture | null = null
|
||||
|
|
@ -1404,6 +1424,11 @@ function addArmorModel (worldRenderer: WorldRendererThree, entityMesh: THREE.Obj
|
|||
if (textureData) {
|
||||
const decodedData = JSON.parse(Buffer.from(textureData, 'base64').toString())
|
||||
texturePath = decodedData.textures?.SKIN?.url
|
||||
const { skinTexturesProxy } = this.worldRenderer.worldRendererConfig
|
||||
if (skinTexturesProxy) {
|
||||
texturePath = texturePath?.replace('http://textures.minecraft.net/', skinTexturesProxy)
|
||||
.replace('https://textures.minecraft.net/', skinTexturesProxy)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error decoding player head texture:', err)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ export const renderSlot = (model: ResolvedItemModelRender, resourcesManager: Res
|
|||
scale?: number,
|
||||
slice?: number[],
|
||||
modelName?: string,
|
||||
image?: ImageBitmap
|
||||
} | undefined => {
|
||||
let itemModelName = model.modelName
|
||||
const isItem = loadedData.itemsByName[itemModelName]
|
||||
|
|
@ -36,7 +35,6 @@ export const renderSlot = (model: ResolvedItemModelRender, resourcesManager: Res
|
|||
const y = item.v * atlas.height
|
||||
return {
|
||||
texture: 'gui',
|
||||
image: resourcesManager.currentResources!.guiAtlas!.image,
|
||||
slice: [x, y, atlas.tileSize, atlas.tileSize],
|
||||
scale: 0.25,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@ export class ThreeJsSound implements SoundSystem {
|
|||
sound.position.set(position.x, position.y, position.z)
|
||||
sound.onEnded = () => {
|
||||
this.worldRenderer.scene.remove(sound)
|
||||
sound.disconnect()
|
||||
if (sound.source) {
|
||||
sound.disconnect()
|
||||
}
|
||||
this.activeSounds.delete(sound)
|
||||
audioLoader.manager.itemEnd(path)
|
||||
}
|
||||
|
|
@ -51,7 +53,9 @@ export class ThreeJsSound implements SoundSystem {
|
|||
// Stop and clean up all active sounds
|
||||
for (const sound of this.activeSounds) {
|
||||
sound.stop()
|
||||
sound.disconnect()
|
||||
if (sound.source) {
|
||||
sound.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
// Remove and cleanup audio listener
|
||||
|
|
|
|||
|
|
@ -775,7 +775,12 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
|
||||
try {
|
||||
const textureData = JSON.parse(Buffer.from(textures.Value, 'base64').toString())
|
||||
const skinUrl = textureData.textures?.SKIN?.url
|
||||
let skinUrl = textureData.textures?.SKIN?.url
|
||||
const { skinTexturesProxy } = this.worldRendererConfig
|
||||
if (skinTexturesProxy) {
|
||||
skinUrl = skinUrl?.replace('http://textures.minecraft.net/', skinTexturesProxy)
|
||||
.replace('https://textures.minecraft.net/', skinTexturesProxy)
|
||||
}
|
||||
|
||||
const mesh = getMesh(this, skinUrl, armorModel.head)
|
||||
const group = new THREE.Group()
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference types="./src/env" />
|
||||
import { defineConfig, mergeRsbuildConfig, RsbuildPluginAPI } from '@rsbuild/core'
|
||||
import { pluginReact } from '@rsbuild/plugin-react'
|
||||
import { pluginTypedCSSModules } from '@rsbuild/plugin-typed-css-modules'
|
||||
|
|
@ -14,6 +15,7 @@ import { appAndRendererSharedConfig } from './renderer/rsbuildSharedConfig'
|
|||
import { genLargeDataAliases } from './scripts/genLargeDataAliases'
|
||||
import sharp from 'sharp'
|
||||
import supportedVersions from './src/supportedVersions.mjs'
|
||||
import { startWsServer } from './scripts/wsServer'
|
||||
|
||||
const SINGLE_FILE_BUILD = process.env.SINGLE_FILE_BUILD === 'true'
|
||||
|
||||
|
|
@ -48,7 +50,7 @@ if (fs.existsSync('./assets/release.json')) {
|
|||
|
||||
const configJson = JSON.parse(fs.readFileSync('./config.json', 'utf8'))
|
||||
try {
|
||||
Object.assign(configJson, JSON.parse(fs.readFileSync('./config.local.json', 'utf8')))
|
||||
Object.assign(configJson, JSON.parse(fs.readFileSync(process.env.LOCAL_CONFIG_FILE || './config.local.json', 'utf8')))
|
||||
} catch (err) {}
|
||||
if (dev) {
|
||||
configJson.defaultProxy = ':8080'
|
||||
|
|
@ -58,6 +60,8 @@ const configSource = (SINGLE_FILE_BUILD ? 'BUNDLED' : (process.env.CONFIG_JSON_S
|
|||
|
||||
const faviconPath = 'favicon.png'
|
||||
|
||||
const enableMetrics = process.env.ENABLE_METRICS === 'true'
|
||||
|
||||
// base options are in ./renderer/rsbuildSharedConfig.ts
|
||||
const appConfig = defineConfig({
|
||||
html: {
|
||||
|
|
@ -111,6 +115,22 @@ const appConfig = defineConfig({
|
|||
js: 'source-map',
|
||||
css: true,
|
||||
},
|
||||
minify: {
|
||||
// js: false,
|
||||
jsOptions: {
|
||||
minimizerOptions: {
|
||||
mangle: {
|
||||
safari10: true,
|
||||
keep_classnames: true,
|
||||
keep_fnames: true,
|
||||
keep_private_props: true,
|
||||
},
|
||||
compress: {
|
||||
unused: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
distPath: SINGLE_FILE_BUILD ? {
|
||||
html: './single',
|
||||
} : undefined,
|
||||
|
|
@ -141,6 +161,8 @@ const appConfig = defineConfig({
|
|||
'process.env.DISABLE_SERVICE_WORKER': JSON.stringify(disableServiceWorker),
|
||||
'process.env.INLINED_APP_CONFIG': JSON.stringify(configSource === 'BUNDLED' ? configJson : null),
|
||||
'process.env.ENABLE_COOKIE_STORAGE': JSON.stringify(process.env.ENABLE_COOKIE_STORAGE || true),
|
||||
'process.env.COOKIE_STORAGE_PREFIX': JSON.stringify(process.env.COOKIE_STORAGE_PREFIX || ''),
|
||||
'process.env.WS_PORT': JSON.stringify(enableMetrics ? 8081 : false),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
|
|
@ -175,13 +197,14 @@ const appConfig = defineConfig({
|
|||
fs.copyFileSync('./assets/playground.html', './dist/playground.html')
|
||||
fs.copyFileSync('./assets/manifest.json', './dist/manifest.json')
|
||||
fs.copyFileSync('./assets/config.html', './dist/config.html')
|
||||
fs.copyFileSync('./assets/debug-inputs.html', './dist/debug-inputs.html')
|
||||
fs.copyFileSync('./assets/loading-bg.jpg', './dist/loading-bg.jpg')
|
||||
if (fs.existsSync('./assets/release.json')) {
|
||||
fs.copyFileSync('./assets/release.json', './dist/release.json')
|
||||
}
|
||||
|
||||
if (configSource === 'REMOTE') {
|
||||
fs.writeFileSync('./dist/config.json', JSON.stringify(configJson), 'utf8')
|
||||
fs.writeFileSync('./dist/config.json', JSON.stringify(configJson, undefined, 2), 'utf8')
|
||||
}
|
||||
if (fs.existsSync('./generated/sounds.js')) {
|
||||
fs.copyFileSync('./generated/sounds.js', './dist/sounds.js')
|
||||
|
|
@ -197,6 +220,12 @@ const appConfig = defineConfig({
|
|||
await execAsync('pnpm run build-mesher')
|
||||
}
|
||||
fs.writeFileSync('./dist/version.txt', buildingVersion, 'utf-8')
|
||||
|
||||
// Start WebSocket server in development
|
||||
if (dev && enableMetrics) {
|
||||
await startWsServer(8081, false)
|
||||
}
|
||||
|
||||
console.timeEnd('total-prep')
|
||||
}
|
||||
if (!dev) {
|
||||
|
|
|
|||
42
scripts/requestData.ts
Normal file
42
scripts/requestData.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import WebSocket from 'ws'
|
||||
|
||||
function formatBytes(bytes: number) {
|
||||
return `${(bytes).toFixed(2)} MB`
|
||||
}
|
||||
|
||||
function formatTime(ms: number) {
|
||||
return `${(ms / 1000).toFixed(2)}s`
|
||||
}
|
||||
|
||||
const ws = new WebSocket('ws://localhost:8081')
|
||||
|
||||
ws.on('open', () => {
|
||||
console.log('Connected to metrics server, waiting for metrics...')
|
||||
})
|
||||
|
||||
ws.on('message', (data) => {
|
||||
try {
|
||||
const metrics = JSON.parse(data.toString())
|
||||
console.log('\nPerformance Metrics:')
|
||||
console.log('------------------')
|
||||
console.log(`Load Time: ${formatTime(metrics.loadTime)}`)
|
||||
console.log(`Memory Usage: ${formatBytes(metrics.memoryUsage)}`)
|
||||
console.log(`Timestamp: ${new Date(metrics.timestamp).toLocaleString()}`)
|
||||
if (!process.argv.includes('-f')) { // follow mode
|
||||
process.exit(0)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing metrics:', error)
|
||||
}
|
||||
})
|
||||
|
||||
ws.on('error', (error) => {
|
||||
console.error('WebSocket error:', error)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
// Exit if no metrics received after 5 seconds
|
||||
setTimeout(() => {
|
||||
console.error('Timeout waiting for metrics')
|
||||
process.exit(1)
|
||||
}, 5000)
|
||||
160
scripts/updateGitDeps.ts
Normal file
160
scripts/updateGitDeps.ts
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import yaml from 'yaml'
|
||||
import { execSync } from 'child_process'
|
||||
import { createInterface } from 'readline'
|
||||
|
||||
interface LockfilePackage {
|
||||
specifier: string
|
||||
version: string
|
||||
}
|
||||
|
||||
interface Lockfile {
|
||||
importers: {
|
||||
'.': {
|
||||
dependencies?: Record<string, LockfilePackage>
|
||||
devDependencies?: Record<string, LockfilePackage>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface PackageJson {
|
||||
pnpm?: {
|
||||
updateConfig?: {
|
||||
ignoreDependencies?: string[]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function prompt(question: string): Promise<string> {
|
||||
const rl = createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
})
|
||||
|
||||
return new Promise(resolve => {
|
||||
rl.question(question, answer => {
|
||||
rl.close()
|
||||
resolve(answer.toLowerCase().trim())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function getLatestCommit(owner: string, repo: string): Promise<string> {
|
||||
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/commits/HEAD`)
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch latest commit: ${response.statusText}`)
|
||||
}
|
||||
const data = await response.json()
|
||||
return data.sha
|
||||
}
|
||||
|
||||
function extractGitInfo(specifier: string): { owner: string; repo: string; branch: string } | null {
|
||||
const match = specifier.match(/github:([^/]+)\/([^#]+)(?:#(.+))?/)
|
||||
if (!match) return null
|
||||
return {
|
||||
owner: match[1],
|
||||
repo: match[2],
|
||||
branch: match[3] || 'master'
|
||||
}
|
||||
}
|
||||
|
||||
function extractCommitHash(version: string): string | null {
|
||||
const match = version.match(/https:\/\/codeload\.github\.com\/[^/]+\/[^/]+\/tar\.gz\/([a-f0-9]+)/)
|
||||
return match ? match[1] : null
|
||||
}
|
||||
|
||||
function getIgnoredDependencies(): string[] {
|
||||
try {
|
||||
const packageJsonPath = path.join(process.cwd(), 'package.json')
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) as PackageJson
|
||||
return packageJson.pnpm?.updateConfig?.ignoreDependencies || []
|
||||
} catch (error) {
|
||||
console.warn('Failed to read package.json for ignored dependencies:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const lockfilePath = path.join(process.cwd(), 'pnpm-lock.yaml')
|
||||
const lockfileContent = fs.readFileSync(lockfilePath, 'utf8')
|
||||
const lockfile = yaml.parse(lockfileContent) as Lockfile
|
||||
|
||||
const ignoredDependencies = new Set(getIgnoredDependencies())
|
||||
console.log('Ignoring dependencies:', Array.from(ignoredDependencies).join(', ') || 'none')
|
||||
|
||||
const dependencies = {
|
||||
...lockfile.importers['.'].dependencies,
|
||||
...lockfile.importers['.'].devDependencies
|
||||
}
|
||||
|
||||
const updates: Array<{
|
||||
name: string
|
||||
currentHash: string
|
||||
latestHash: string
|
||||
gitInfo: ReturnType<typeof extractGitInfo>
|
||||
}> = []
|
||||
|
||||
console.log('\nChecking git dependencies...')
|
||||
for (const [name, pkg] of Object.entries(dependencies)) {
|
||||
if (ignoredDependencies.has(name)) {
|
||||
console.log(`Skipping ignored dependency: ${name}`)
|
||||
continue
|
||||
}
|
||||
|
||||
if (!pkg.specifier.startsWith('github:')) continue
|
||||
|
||||
const gitInfo = extractGitInfo(pkg.specifier)
|
||||
if (!gitInfo) continue
|
||||
|
||||
const currentHash = extractCommitHash(pkg.version)
|
||||
if (!currentHash) continue
|
||||
|
||||
try {
|
||||
process.stdout.write(`Checking ${name}... `)
|
||||
const latestHash = await getLatestCommit(gitInfo.owner, gitInfo.repo)
|
||||
if (currentHash !== latestHash) {
|
||||
console.log('update available')
|
||||
updates.push({ name, currentHash, latestHash, gitInfo })
|
||||
} else {
|
||||
console.log('up to date')
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('failed')
|
||||
console.error(`Error checking ${name}:`, error)
|
||||
}
|
||||
}
|
||||
|
||||
if (updates.length === 0) {
|
||||
console.log('\nAll git dependencies are up to date!')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('\nThe following git dependencies can be updated:')
|
||||
for (const update of updates) {
|
||||
console.log(`\n${update.name}:`)
|
||||
console.log(` Current: ${update.currentHash}`)
|
||||
console.log(` Latest: ${update.latestHash}`)
|
||||
console.log(` Repo: ${update.gitInfo!.owner}/${update.gitInfo!.repo}`)
|
||||
}
|
||||
|
||||
const answer = await prompt('\nWould you like to update these dependencies? (y/N): ')
|
||||
if (answer === 'y' || answer === 'yes') {
|
||||
let newLockfileContent = lockfileContent
|
||||
for (const update of updates) {
|
||||
newLockfileContent = newLockfileContent.replace(
|
||||
new RegExp(update.currentHash, 'g'),
|
||||
update.latestHash
|
||||
)
|
||||
}
|
||||
fs.writeFileSync(lockfilePath, newLockfileContent)
|
||||
console.log('\nUpdated pnpm-lock.yaml with new commit hashes')
|
||||
// console.log('Running pnpm install to apply changes...')
|
||||
// execSync('pnpm install', { stdio: 'inherit' })
|
||||
console.log('Done!')
|
||||
} else {
|
||||
console.log('\nNo changes were made.')
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error)
|
||||
45
scripts/wsServer.ts
Normal file
45
scripts/wsServer.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import {WebSocketServer} from 'ws'
|
||||
|
||||
export function startWsServer(port: number = 8081, tryOtherPort: boolean = true): Promise<number> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const tryPort = (currentPort: number) => {
|
||||
const wss = new WebSocketServer({ port: currentPort })
|
||||
.on('listening', () => {
|
||||
console.log(`WebSocket server started on port ${currentPort}`)
|
||||
resolve(currentPort)
|
||||
})
|
||||
.on('error', (err: any) => {
|
||||
if (err.code === 'EADDRINUSE' && tryOtherPort) {
|
||||
console.log(`Port ${currentPort} in use, trying ${currentPort + 1}`)
|
||||
wss.close()
|
||||
tryPort(currentPort + 1)
|
||||
} else {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
|
||||
wss.on('connection', (ws) => {
|
||||
console.log('Client connected')
|
||||
|
||||
ws.on('message', (message) => {
|
||||
try {
|
||||
// Simply relay the message to all connected clients except sender
|
||||
wss.clients.forEach(client => {
|
||||
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
||||
client.send(message.toString())
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error processing message:', error)
|
||||
}
|
||||
})
|
||||
|
||||
ws.on('close', () => {
|
||||
console.log('Client disconnected')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
tryPort(port)
|
||||
})
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ export type MobileButtonConfig = {
|
|||
readonly icon?: string
|
||||
readonly action?: ActionType
|
||||
readonly actionHold?: ActionType | ActionHoldConfig
|
||||
readonly iconStyle?: React.CSSProperties
|
||||
}
|
||||
|
||||
export type AppConfig = {
|
||||
|
|
@ -54,9 +55,14 @@ export type AppConfig = {
|
|||
supportedLanguages?: string[]
|
||||
showModsButton?: boolean
|
||||
defaultUsername?: string
|
||||
skinTexturesProxy?: string
|
||||
alwaysReconnectButton?: boolean
|
||||
reportBugButtonWithReconnect?: boolean
|
||||
disabledCommands?: string[] // Array of command IDs to disable (e.g. ['general.jump', 'general.chat'])
|
||||
}
|
||||
|
||||
export const loadAppConfig = (appConfig: AppConfig) => {
|
||||
|
||||
if (miscUiState.appConfig) {
|
||||
Object.assign(miscUiState.appConfig, appConfig)
|
||||
} else {
|
||||
|
|
@ -68,7 +74,7 @@ export const loadAppConfig = (appConfig: AppConfig) => {
|
|||
if (value) {
|
||||
disabledSettings.value.add(key)
|
||||
// since the setting is forced, we need to set it to that value
|
||||
if (appConfig.defaultSettings?.[key] && !qsOptions[key]) {
|
||||
if (appConfig.defaultSettings && key in appConfig.defaultSettings && !qsOptions[key]) {
|
||||
options[key] = appConfig.defaultSettings[key]
|
||||
}
|
||||
} else {
|
||||
|
|
@ -76,12 +82,15 @@ export const loadAppConfig = (appConfig: AppConfig) => {
|
|||
}
|
||||
}
|
||||
}
|
||||
// todo apply defaultSettings to defaults even if not forced in case of remote config
|
||||
|
||||
if (appConfig.keybindings) {
|
||||
Object.assign(customKeymaps, defaultsDeep(appConfig.keybindings, customKeymaps))
|
||||
updateBinds(customKeymaps)
|
||||
}
|
||||
|
||||
appViewer?.appConfigUdpate()
|
||||
|
||||
setStorageDataOnAppConfigLoad(appConfig)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { resetStateAfterDisconnect } from './browserfs'
|
||||
import { hideModal, activeModalStack, showModal, miscUiState } from './globalState'
|
||||
import { appStatusState, resetAppStatusState } from './react/AppStatusProvider'
|
||||
|
||||
|
|
@ -25,7 +26,6 @@ export const setLoadingScreenStatus = function (status: string | undefined | nul
|
|||
}
|
||||
showModal({ reactType: 'app-status' })
|
||||
if (appStatusState.isError) {
|
||||
miscUiState.gameLoaded = false
|
||||
return
|
||||
}
|
||||
appStatusState.hideDots = hideDots
|
||||
|
|
@ -33,5 +33,9 @@ export const setLoadingScreenStatus = function (status: string | undefined | nul
|
|||
appStatusState.lastStatus = isError ? appStatusState.status : ''
|
||||
appStatusState.status = status
|
||||
appStatusState.minecraftJsonMessage = minecraftJsonMessage ?? null
|
||||
|
||||
if (isError && miscUiState.gameLoaded) {
|
||||
resetStateAfterDisconnect()
|
||||
}
|
||||
}
|
||||
globalThis.setLoadingScreenStatus = setLoadingScreenStatus
|
||||
|
|
|
|||
|
|
@ -180,6 +180,12 @@ export class AppViewer {
|
|||
this.worldView!.listenToBot(bot)
|
||||
}
|
||||
|
||||
appConfigUdpate () {
|
||||
if (miscUiState.appConfig) {
|
||||
this.inWorldRenderingConfig.skinTexturesProxy = miscUiState.appConfig.skinTexturesProxy
|
||||
}
|
||||
}
|
||||
|
||||
async startWorld (world, renderDistance: number, playerStateSend: PlayerStateRenderer = this.playerState.reactive) {
|
||||
if (this.currentDisplay === 'world') throw new Error('World already started')
|
||||
this.currentDisplay = 'world'
|
||||
|
|
@ -187,6 +193,7 @@ export class AppViewer {
|
|||
this.worldView = new WorldDataEmitter(world, renderDistance, startPosition)
|
||||
window.worldView = this.worldView
|
||||
watchOptionsAfterWorldViewInit(this.worldView)
|
||||
this.appConfigUdpate()
|
||||
|
||||
const displayWorldOptions: DisplayWorldOptions = {
|
||||
version: this.resourcesManager.currentConfig!.version,
|
||||
|
|
|
|||
|
|
@ -116,6 +116,10 @@ export const contro = new ControMax({
|
|||
window.controMax = contro
|
||||
export type Command = CommandEventArgument<typeof contro['_commandsRaw']>['command']
|
||||
|
||||
export const isCommandDisabled = (command: Command) => {
|
||||
return miscUiState.appConfig?.disabledCommands?.includes(command)
|
||||
}
|
||||
|
||||
onControInit()
|
||||
|
||||
updateBinds(customKeymaps)
|
||||
|
|
@ -544,6 +548,8 @@ const customCommandsHandler = ({ command }) => {
|
|||
contro.on('trigger', customCommandsHandler)
|
||||
|
||||
contro.on('trigger', ({ command }) => {
|
||||
if (isCommandDisabled(command)) return
|
||||
|
||||
const willContinue = !isGameActive(true)
|
||||
alwaysPressedHandledCommand(command)
|
||||
if (willContinue) return
|
||||
|
|
@ -677,6 +683,8 @@ contro.on('trigger', ({ command }) => {
|
|||
})
|
||||
|
||||
contro.on('release', ({ command }) => {
|
||||
if (isCommandDisabled(command)) return
|
||||
|
||||
inModalCommand(command, false)
|
||||
onTriggerOrReleased(command, false)
|
||||
})
|
||||
|
|
@ -996,9 +1004,16 @@ export const handleMobileButtonCustomAction = (action: CustomAction) => {
|
|||
}
|
||||
}
|
||||
|
||||
export const triggerCommand = (command: Command, isDown: boolean) => {
|
||||
handleMobileButtonActionCommand(command, isDown)
|
||||
}
|
||||
|
||||
export const handleMobileButtonActionCommand = (command: ActionType | ActionHoldConfig, isDown: boolean) => {
|
||||
const commandValue = typeof command === 'string' ? command : 'command' in command ? command.command : command
|
||||
|
||||
// Check if command is disabled before proceeding
|
||||
if (typeof commandValue === 'string' && isCommandDisabled(commandValue as Command)) return
|
||||
|
||||
if (typeof commandValue === 'string' && !stringStartsWith(commandValue, 'custom')) {
|
||||
const event: CommandEventArgument<typeof contro['_commandsRaw']> = {
|
||||
command: commandValue as Command,
|
||||
|
|
|
|||
103
src/devtools.ts
103
src/devtools.ts
|
|
@ -209,3 +209,106 @@ setInterval(() => {
|
|||
}, 1000)
|
||||
|
||||
// ---
|
||||
|
||||
// Add type declaration for performance.memory
|
||||
declare global {
|
||||
interface Performance {
|
||||
memory?: {
|
||||
usedJSHeapSize: number
|
||||
totalJSHeapSize: number
|
||||
jsHeapSizeLimit: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Performance metrics WebSocket client
|
||||
let ws: WebSocket | null = null
|
||||
let wsReconnectTimeout: NodeJS.Timeout | null = null
|
||||
let metricsInterval: NodeJS.Timeout | null = null
|
||||
|
||||
// Start collecting metrics immediately
|
||||
const startTime = performance.now()
|
||||
|
||||
function collectAndSendMetrics () {
|
||||
if (!ws || ws.readyState !== WebSocket.OPEN) return
|
||||
|
||||
const metrics = {
|
||||
loadTime: performance.now() - startTime,
|
||||
memoryUsage: (performance.memory?.usedJSHeapSize ?? 0) / 1024 / 1024,
|
||||
timestamp: Date.now()
|
||||
}
|
||||
|
||||
ws.send(JSON.stringify(metrics))
|
||||
}
|
||||
|
||||
function getWebSocketUrl () {
|
||||
const wsPort = process.env.WS_PORT
|
||||
if (!wsPort) return null
|
||||
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||
const { hostname } = window.location
|
||||
return `${protocol}//${hostname}:${wsPort}`
|
||||
}
|
||||
|
||||
function connectWebSocket () {
|
||||
if (ws) return
|
||||
|
||||
const wsUrl = getWebSocketUrl()
|
||||
if (!wsUrl) {
|
||||
console.log('WebSocket server not configured')
|
||||
return
|
||||
}
|
||||
|
||||
ws = new WebSocket(wsUrl)
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log('Connected to metrics server')
|
||||
if (wsReconnectTimeout) {
|
||||
clearTimeout(wsReconnectTimeout)
|
||||
wsReconnectTimeout = null
|
||||
}
|
||||
|
||||
// Start sending metrics immediately after connection
|
||||
collectAndSendMetrics()
|
||||
|
||||
// Clear existing interval if any
|
||||
if (metricsInterval) {
|
||||
clearInterval(metricsInterval)
|
||||
}
|
||||
|
||||
// Set new interval
|
||||
metricsInterval = setInterval(collectAndSendMetrics, 500)
|
||||
}
|
||||
|
||||
ws.onclose = () => {
|
||||
console.log('Disconnected from metrics server')
|
||||
ws = null
|
||||
|
||||
// Clear metrics interval
|
||||
if (metricsInterval) {
|
||||
clearInterval(metricsInterval)
|
||||
metricsInterval = null
|
||||
}
|
||||
|
||||
// Try to reconnect after 3 seconds
|
||||
wsReconnectTimeout = setTimeout(connectWebSocket, 3000)
|
||||
}
|
||||
|
||||
ws.onerror = (error) => {
|
||||
console.error('WebSocket error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// Connect immediately
|
||||
connectWebSocket()
|
||||
|
||||
// Add command to request current metrics
|
||||
window.requestMetrics = () => {
|
||||
const metrics = {
|
||||
loadTime: performance.now() - startTime,
|
||||
memoryUsage: (performance.memory?.usedJSHeapSize ?? 0) / 1024 / 1024,
|
||||
timestamp: Date.now()
|
||||
}
|
||||
console.log('Current metrics:', metrics)
|
||||
return metrics
|
||||
}
|
||||
|
|
|
|||
119
src/entities.ts
119
src/entities.ts
|
|
@ -4,6 +4,7 @@ import tracker from '@nxg-org/mineflayer-tracker'
|
|||
import { loader as autoJumpPlugin } from '@nxg-org/mineflayer-auto-jump'
|
||||
import { subscribeKey } from 'valtio/utils'
|
||||
import { getThreeJsRendererMethods } from 'renderer/viewer/three/threeJsMethods'
|
||||
import { Team } from 'mineflayer'
|
||||
import { options, watchValue } from './optionsStorage'
|
||||
import { gameAdditionalState, miscUiState } from './globalState'
|
||||
import { EntityStatus } from './mineflayer/entityStatus'
|
||||
|
|
@ -197,35 +198,101 @@ customEvents.on('gameLoaded', () => {
|
|||
}
|
||||
})
|
||||
|
||||
// Texture override from packet properties
|
||||
bot._client.on('player_info', (packet) => {
|
||||
for (const playerEntry of packet.data) {
|
||||
if (!playerEntry.player && !playerEntry.properties) continue
|
||||
let textureProperty = playerEntry.properties?.find(prop => prop?.name === 'textures')
|
||||
if (!textureProperty) {
|
||||
textureProperty = playerEntry.player?.properties?.find(prop => prop?.key === 'textures')
|
||||
}
|
||||
if (textureProperty) {
|
||||
try {
|
||||
const textureData = JSON.parse(Buffer.from(textureProperty.value, 'base64').toString())
|
||||
const skinUrl = textureData.textures?.SKIN?.url
|
||||
const capeUrl = textureData.textures?.CAPE?.url
|
||||
const applySkinTexturesProxy = (url: string | undefined) => {
|
||||
const { appConfig } = miscUiState
|
||||
if (appConfig?.skinTexturesProxy) {
|
||||
return url?.replace('http://textures.minecraft.net/', appConfig.skinTexturesProxy)
|
||||
.replace('https://textures.minecraft.net/', appConfig.skinTexturesProxy)
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
// Find entity with matching UUID and update skin
|
||||
let entityId = ''
|
||||
for (const [entId, entity] of Object.entries(bot.entities)) {
|
||||
if (entity.uuid === playerEntry.uuid) {
|
||||
entityId = entId
|
||||
break
|
||||
}
|
||||
}
|
||||
// even if not found, still record to cache
|
||||
void getThreeJsRendererMethods()?.updatePlayerSkin(entityId, playerEntry.player?.name, playerEntry.uuid, skinUrl, capeUrl)
|
||||
} catch (err) {
|
||||
console.error('Error decoding player texture:', err)
|
||||
// Texture override from packet properties
|
||||
const updateSkin = (player: import('mineflayer').Player) => {
|
||||
if (!player.uuid || !player.username || !player.skinData) return
|
||||
|
||||
try {
|
||||
const skinUrl = applySkinTexturesProxy(player.skinData.url)
|
||||
const capeUrl = applySkinTexturesProxy((player.skinData as any).capeUrl)
|
||||
|
||||
// Find entity with matching UUID and update skin
|
||||
let entityId = ''
|
||||
for (const [entId, entity] of Object.entries(bot.entities)) {
|
||||
if (entity.uuid === player.uuid) {
|
||||
entityId = entId
|
||||
break
|
||||
}
|
||||
}
|
||||
// even if not found, still record to cache
|
||||
void getThreeJsRendererMethods()?.updatePlayerSkin(entityId, player.username, player.uuid, skinUrl ?? true, capeUrl)
|
||||
} catch (err) {
|
||||
console.error('Error decoding player texture:', err)
|
||||
}
|
||||
}
|
||||
|
||||
bot.on('playerJoined', updateSkin)
|
||||
bot.on('playerUpdated', updateSkin)
|
||||
|
||||
bot.on('teamUpdated', (team: Team) => {
|
||||
for (const entity of Object.values(bot.entities)) {
|
||||
if (entity.type === 'player' && entity.username && team.members.includes(entity.username) || entity.uuid && team.members.includes(entity.uuid)) {
|
||||
bot.emit('entityUpdate', entity)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const updateEntityNameTags = (team: Team) => {
|
||||
for (const entity of Object.values(bot.entities)) {
|
||||
const entityTeam = entity.type === 'player' && entity.username ? bot.teamMap[entity.username] : entity.uuid ? bot.teamMap[entity.uuid] : undefined
|
||||
if ((entityTeam?.nameTagVisibility === 'hideForOwnTeam' && entityTeam.name === team.name)
|
||||
|| (entityTeam?.nameTagVisibility === 'hideForOtherTeams' && entityTeam.name !== team.name)) {
|
||||
bot.emit('entityUpdate', entity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const doEntitiesNeedUpdating = (team: Team) => {
|
||||
return team.nameTagVisibility === 'never'
|
||||
|| (team.nameTagVisibility === 'hideForOtherTeams' && appViewer.playerState.reactive.team?.team !== team.team)
|
||||
|| (team.nameTagVisibility === 'hideForOwnTeam' && appViewer.playerState.reactive.team?.team === team.team)
|
||||
}
|
||||
|
||||
bot.on('teamMemberAdded', (team: Team, members: string[]) => {
|
||||
if (members.includes(bot.username) && appViewer.playerState.reactive.team?.team !== team.team) {
|
||||
appViewer.playerState.reactive.team = team
|
||||
// Player was added to a team, need to check if any entities need updating
|
||||
updateEntityNameTags(team)
|
||||
} else if (doEntitiesNeedUpdating(team)) {
|
||||
// Need to update all entities that were added
|
||||
for (const entity of Object.values(bot.entities)) {
|
||||
if (entity.type === 'player' && entity.username && members.includes(entity.username) || entity.uuid && members.includes(entity.uuid)) {
|
||||
bot.emit('entityUpdate', entity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
bot.on('teamMemberRemoved', (team: Team, members: string[]) => {
|
||||
if (members.includes(bot.username) && appViewer.playerState.reactive.team?.team === team.team) {
|
||||
appViewer.playerState.reactive.team = undefined
|
||||
// Player was removed from a team, need to check if any entities need updating
|
||||
updateEntityNameTags(team)
|
||||
} else if (doEntitiesNeedUpdating(team)) {
|
||||
// Need to update all entities that were removed
|
||||
for (const entity of Object.values(bot.entities)) {
|
||||
if (entity.type === 'player' && entity.username && members.includes(entity.username) || entity.uuid && members.includes(entity.uuid)) {
|
||||
bot.emit('entityUpdate', entity)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
bot.on('teamRemoved', (team: Team) => {
|
||||
if (appViewer.playerState.reactive.team?.team === team.team) {
|
||||
appViewer.playerState.reactive.team = undefined
|
||||
// Player's team was removed, need to update all entities that are in a team
|
||||
updateEntityNameTags(team)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
|
|
|||
31
src/env.d.ts
vendored
Normal file
31
src/env.d.ts
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
declare namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
// Build configuration
|
||||
NODE_ENV: 'development' | 'production'
|
||||
SINGLE_FILE_BUILD?: string
|
||||
WS_PORT?: string
|
||||
DISABLE_SERVICE_WORKER?: string
|
||||
CONFIG_JSON_SOURCE?: 'BUNDLED' | 'REMOTE'
|
||||
LOCAL_CONFIG_FILE?: string
|
||||
BUILD_VERSION?: string
|
||||
|
||||
// GitHub and Vercel related
|
||||
GITHUB_REPOSITORY?: string
|
||||
VERCEL_GIT_REPO_OWNER?: string
|
||||
VERCEL_GIT_REPO_SLUG?: string
|
||||
|
||||
// UI and Features
|
||||
MAIN_MENU_LINKS?: string
|
||||
ENABLE_COOKIE_STORAGE?: string
|
||||
COOKIE_STORAGE_PREFIX?: string
|
||||
|
||||
// Release information
|
||||
RELEASE_TAG?: string
|
||||
RELEASE_LINK?: string
|
||||
RELEASE_CHANGELOG?: string
|
||||
|
||||
// Other configurations
|
||||
DEPS_VERSIONS?: string
|
||||
INLINED_APP_CONFIG?: string
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,8 @@ window.bot = undefined
|
|||
window.THREE = undefined
|
||||
window.localServer = undefined
|
||||
window.worldView = undefined
|
||||
window.viewer = undefined
|
||||
window.viewer = undefined // legacy
|
||||
window.appViewer = undefined
|
||||
window.loadedData = undefined
|
||||
window.customEvents = new EventEmitter()
|
||||
window.customEvents.setMaxListeners(10_000)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import { MessageFormatPart } from './chatUtils'
|
|||
import { GeneralInputItem, getItemMetadata, getItemModelName, getItemNameRaw, RenderItem } from './mineflayer/items'
|
||||
import { playerState } from './mineflayer/playerState'
|
||||
|
||||
const loadedImagesCache = new Map<string, HTMLImageElement>()
|
||||
const loadedImagesCache = new Map<string, HTMLImageElement | ImageBitmap>()
|
||||
const cleanLoadedImagesCache = () => {
|
||||
loadedImagesCache.delete('blocks')
|
||||
loadedImagesCache.delete('items')
|
||||
|
|
@ -132,11 +132,12 @@ export const onGameLoad = () => {
|
|||
}
|
||||
}
|
||||
|
||||
const getImageSrc = (path): string | HTMLImageElement => {
|
||||
const getImageSrc = (path): string | HTMLImageElement | ImageBitmap => {
|
||||
switch (path) {
|
||||
case 'gui/container/inventory': return appReplacableResources.latest_gui_container_inventory.content
|
||||
case 'blocks': return appViewer.resourcesManager.blocksAtlasParser.latestImage
|
||||
case 'items': return appViewer.resourcesManager.itemsAtlasParser.latestImage
|
||||
case 'gui': return appViewer.resourcesManager.currentResources!.guiAtlas!.image
|
||||
case 'gui/container/dispenser': return appReplacableResources.latest_gui_container_dispenser.content
|
||||
case 'gui/container/furnace': return appReplacableResources.latest_gui_container_furnace.content
|
||||
case 'gui/container/crafting_table': return appReplacableResources.latest_gui_container_crafting_table.content
|
||||
|
|
@ -167,6 +168,12 @@ const getImage = ({ path = undefined as string | undefined, texture = undefined
|
|||
onLoad()
|
||||
} else {
|
||||
const imageSrc = getImageSrc(loadPath)
|
||||
if (imageSrc instanceof ImageBitmap) {
|
||||
onLoad()
|
||||
loadedImagesCache.set(loadPath, imageSrc)
|
||||
return imageSrc
|
||||
}
|
||||
|
||||
let image: HTMLImageElement
|
||||
if (imageSrc instanceof Image) {
|
||||
image = imageSrc
|
||||
|
|
@ -277,6 +284,7 @@ const implementedContainersGuiMap = {
|
|||
'minecraft:generic_3x3': 'DropDispenseWin',
|
||||
'minecraft:furnace': 'FurnaceWin',
|
||||
'minecraft:smoker': 'FurnaceWin',
|
||||
'minecraft:shulker_box': 'ChestWin',
|
||||
'minecraft:blast_furnace': 'FurnaceWin',
|
||||
'minecraft:crafting': 'CraftingWin',
|
||||
'minecraft:crafting3x3': 'CraftingWin', // todo different result slot
|
||||
|
|
@ -367,7 +375,7 @@ const openWindow = (type: string | undefined) => {
|
|||
lastWindow.destroy()
|
||||
lastWindow = null as any
|
||||
lastWindowType = null
|
||||
window.lastWindow = lastWindow
|
||||
window.inventory = null
|
||||
miscUiState.displaySearchInput = false
|
||||
destroyFn()
|
||||
skipClosePacketSending = false
|
||||
|
|
@ -375,6 +383,7 @@ const openWindow = (type: string | undefined) => {
|
|||
cleanLoadedImagesCache()
|
||||
const inv = openItemsCanvas(type)
|
||||
inv.canvasManager.children[0].mobileHelpers = miscUiState.currentTouch
|
||||
window.inventory = inv
|
||||
const title = bot.currentWindow?.title
|
||||
const PrismarineChat = PrismarineChatLoader(bot.version)
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -99,6 +99,10 @@ export class PlayerStateControllerMain {
|
|||
})
|
||||
this.reactive.gameMode = bot.game?.gameMode
|
||||
|
||||
customEvents.on('gameLoaded', () => {
|
||||
this.reactive.team = bot.teamMap[bot.username]
|
||||
})
|
||||
|
||||
this.watchReactive()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -545,18 +545,6 @@ export const guiOptionsScheme: {
|
|||
/>
|
||||
}
|
||||
},
|
||||
{
|
||||
custom () {
|
||||
const { cookieStorage } = useSnapshot(appStorage)
|
||||
return <Button
|
||||
label={`Storage: ${cookieStorage ? 'Synced Cookies' : 'Local Storage'}`} onClick={() => {
|
||||
appStorage.cookieStorage = !cookieStorage
|
||||
alert('Reload the page to apply this change')
|
||||
}}
|
||||
inScreen
|
||||
/>
|
||||
}
|
||||
},
|
||||
{
|
||||
custom () {
|
||||
return <Category>Server Connection</Category>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
--offset: calc(-1 * 25px);
|
||||
--bg-x: calc(-1 * 16px);
|
||||
--bg-y: calc(-1 * 9px);
|
||||
pointer-events: none;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
--offset: calc(-1 * 16px);
|
||||
--bg-x: calc(-1 * 16px);
|
||||
--bg-y: calc(-1 * 18px);
|
||||
pointer-events: none;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,12 @@ div.chat-wrapper {
|
|||
/* z-index: 10; */
|
||||
padding-left: calc(env(safe-area-inset-left) / 2);
|
||||
padding-right: calc(env(safe-area-inset-right, 4px) / 2);
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
/* Only apply overflow hidden when not in mobile mode */
|
||||
div.chat-wrapper:not(.display-mobile):not(.input-mobile) {
|
||||
/* overflow: hidden; */
|
||||
}
|
||||
|
||||
.chat-messages-wrapper {
|
||||
|
|
@ -11,12 +17,17 @@ div.chat-wrapper {
|
|||
padding: 4px;
|
||||
padding-left: 0;
|
||||
max-height: var(--chatHeight);
|
||||
width: var(--chatWidth);
|
||||
width: calc(var(--chatWidth) - 5px); /* Custom scrollbar width */
|
||||
transform-origin: bottom left;
|
||||
transform: scale(var(--chatScale));
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Restore full width when chat is opened */
|
||||
.chat-messages-wrapper.chat-opened {
|
||||
width: var(--chatWidth);
|
||||
}
|
||||
|
||||
.chat-input-wrapper {
|
||||
bottom: 1px;
|
||||
width: calc(100% - 3px);
|
||||
|
|
@ -62,7 +73,7 @@ div.chat-wrapper {
|
|||
top: 100%;
|
||||
padding-left: calc(env(safe-area-inset-left) / 2);
|
||||
margin-top: 14px;
|
||||
margin-left: 20px;
|
||||
margin-left: 40px;
|
||||
/* input height */
|
||||
}
|
||||
|
||||
|
|
@ -109,6 +120,11 @@ div.chat-wrapper {
|
|||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.input-mobile .chat-completions-items > div {
|
||||
padding: 4px 0;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.input-mobile {
|
||||
top: 15px;
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ const MessageLine = ({ message, currentPlayerName, chatOpened }: { message: Mess
|
|||
const [fadeState, setFadeState] = useState<'visible' | 'fading' | 'faded'>('visible')
|
||||
|
||||
useEffect(() => {
|
||||
if (window.debugStopChatFade) return
|
||||
// Start fading after 5 seconds
|
||||
const fadeTimeout = setTimeout(() => {
|
||||
setFadeState('fading')
|
||||
|
|
@ -111,7 +112,9 @@ export default ({
|
|||
|
||||
const sendHistoryRef = useRef(JSON.parse(window.sessionStorage.chatHistory || '[]'))
|
||||
const [isInputFocused, setIsInputFocused] = useState(false)
|
||||
const spellCheckEnabled = false
|
||||
const [spellCheckEnabled, setSpellCheckEnabled] = useState(false)
|
||||
const [preservedInputValue, setPreservedInputValue] = useState('')
|
||||
const [inputKey, setInputKey] = useState(0)
|
||||
const pingHistoryRef = useRef(JSON.parse(window.localStorage.pingHistory || '[]'))
|
||||
|
||||
const [completePadText, setCompletePadText] = useState('')
|
||||
|
|
@ -236,12 +239,16 @@ export default ({
|
|||
if (opened) {
|
||||
completeRequestValue.current = ''
|
||||
resetCompletionItems()
|
||||
} else {
|
||||
setPreservedInputValue('')
|
||||
}
|
||||
}, [opened])
|
||||
|
||||
const onMainInputChange = () => {
|
||||
const lastWord = chatInput.current.value.slice(0, chatInput.current.selectionEnd ?? chatInput.current.value.length).split(' ').at(-1)!
|
||||
if (lastWord.startsWith('@') && getPingComplete) {
|
||||
const isCommand = chatInput.current.value.startsWith('/')
|
||||
|
||||
if (lastWord.startsWith('@') && getPingComplete && !isCommand) {
|
||||
setCompletePadText(lastWord)
|
||||
void fetchPingCompletions(true, lastWord.slice(1))
|
||||
return
|
||||
|
|
@ -321,10 +328,33 @@ export default ({
|
|||
return completeValue
|
||||
}
|
||||
|
||||
const handleSlashCommand = () => {
|
||||
remountInput('/')
|
||||
}
|
||||
|
||||
const handleAcceptFirstCompletion = () => {
|
||||
if (completionItems.length > 0) {
|
||||
acceptComplete(completionItems[0])
|
||||
}
|
||||
}
|
||||
|
||||
const remountInput = (newValue?: string) => {
|
||||
if (newValue !== undefined) {
|
||||
setPreservedInputValue(newValue)
|
||||
}
|
||||
setInputKey(k => k + 1)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (preservedInputValue && chatInput.current) {
|
||||
chatInput.current.focus()
|
||||
}
|
||||
}, [inputKey]) // Changed from spellCheckEnabled to inputKey
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`chat-wrapper chat-messages-wrapper ${usingTouch ? 'display-mobile' : ''}`} style={{
|
||||
className={`chat-wrapper chat-messages-wrapper ${usingTouch ? 'display-mobile' : ''} ${opened ? 'chat-opened' : ''}`} style={{
|
||||
userSelect: opened && allowSelection ? 'text' : undefined,
|
||||
}}
|
||||
>
|
||||
|
|
@ -385,8 +415,53 @@ export default ({
|
|||
</div>
|
||||
|
||||
<div className={`chat-wrapper chat-input-wrapper ${usingTouch ? 'input-mobile' : ''}`} hidden={!opened}>
|
||||
{/* close button */}
|
||||
{usingTouch && <Button icon={pixelartIcons.close} onClick={() => onClose?.()} />}
|
||||
{usingTouch && (
|
||||
<>
|
||||
<Button
|
||||
icon={pixelartIcons.close}
|
||||
onClick={() => onClose?.()}
|
||||
style={{
|
||||
width: 20,
|
||||
flexShrink: 0,
|
||||
}}
|
||||
/>
|
||||
|
||||
{(chatInput.current?.value && !chatInput.current.value.startsWith('/')) ? (
|
||||
// TOGGLE SPELL CHECK
|
||||
<Button
|
||||
style={{
|
||||
width: 20,
|
||||
flexShrink: 0,
|
||||
}}
|
||||
overlayColor={spellCheckEnabled ? '#00ff00' : '#ff0000'}
|
||||
icon={pixelartIcons['text-wrap']}
|
||||
onClick={() => {
|
||||
setPreservedInputValue(chatInput.current?.value || '')
|
||||
setSpellCheckEnabled(!spellCheckEnabled)
|
||||
remountInput()
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
// SLASH COMMAND
|
||||
<Button
|
||||
style={{
|
||||
width: 20,
|
||||
flexShrink: 0,
|
||||
}}
|
||||
label={chatInput.current?.value ? undefined : '/'}
|
||||
icon={chatInput.current?.value ? pixelartIcons['arrow-right'] : undefined}
|
||||
onClick={() => {
|
||||
const inputValue = chatInput.current.value
|
||||
if (!inputValue) {
|
||||
handleSlashCommand()
|
||||
} else if (completionItems.length > 0) {
|
||||
handleAcceptFirstCompletion()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<div className="chat-input">
|
||||
{isInputFocused && completionItems?.length ? (
|
||||
<div className="chat-completions">
|
||||
|
|
@ -430,9 +505,10 @@ export default ({
|
|||
/>}
|
||||
<input
|
||||
maxLength={chatVanillaRestrictions ? 256 : undefined}
|
||||
defaultValue=''
|
||||
defaultValue={preservedInputValue}
|
||||
// ios doesn't support toggling autoCorrect on the fly so we need to re-create the input
|
||||
key={spellCheckEnabled ? 'true' : 'false'}
|
||||
key={`${inputKey}`}
|
||||
autoCapitalize={preservedInputValue ? 'off' : 'on'}
|
||||
ref={chatInput}
|
||||
type="text"
|
||||
className="chat-input"
|
||||
|
|
|
|||
|
|
@ -50,20 +50,24 @@ export default () => {
|
|||
if (!options.debugContro) return null
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
position: 'fixed',
|
||||
right: 0,
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
padding: '8px',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '8px',
|
||||
color: 'white',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '4px'
|
||||
}}>
|
||||
<div
|
||||
className='debug-contro'
|
||||
style={{
|
||||
position: 'fixed',
|
||||
right: 0,
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
padding: '8px',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '8px',
|
||||
color: 'white',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '4px',
|
||||
zIndex: 2,
|
||||
}}
|
||||
>
|
||||
<div>Keys: {[...pressedKeys].join(', ')}</div>
|
||||
<div style={{ color: 'limegreen' }}>Actions: {actions.join(', ')}</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
--offset: calc(-1 * (52px + (9px * (4 * var(--kind) + var(--lightened) * 2))));
|
||||
--bg-x: calc(-1 * (16px + 9px * var(--lightened)));
|
||||
--bg-y: calc(-1 * 27px);
|
||||
pointer-events: none;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
--offset: calc(-1 * (52px + (9px * (4 * var(--kind) + var(--lightened) * 2)) ));
|
||||
--bg-x: calc(-1 * (16px + 9px * var(--lightened)));
|
||||
--bg-y: calc(-1 * var(--hardcore) * 45px);
|
||||
pointer-events: none;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@ import { useEffect, useRef, useState } from 'react'
|
|||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { subscribe, useSnapshot } from 'valtio'
|
||||
import { openItemsCanvas, openPlayerInventory, upInventoryItems } from '../inventoryWindows'
|
||||
import { openItemsCanvas, upInventoryItems } from '../inventoryWindows'
|
||||
import { activeModalStack, isGameActive, miscUiState } from '../globalState'
|
||||
import { currentScaling } from '../scaleInterface'
|
||||
import { watchUnloadForCleanup } from '../gameUnload'
|
||||
import { getItemNameRaw } from '../mineflayer/items'
|
||||
import { isInRealGameSession } from '../utils'
|
||||
import { triggerCommand } from '../controls'
|
||||
import MessageFormattedString from './MessageFormattedString'
|
||||
import SharedHudVars from './SharedHudVars'
|
||||
import { packetsReplayState } from './state/packetsReplayState'
|
||||
|
||||
|
||||
const ItemName = ({ itemKey }: { itemKey: string }) => {
|
||||
|
|
@ -75,6 +75,8 @@ const HotbarInner = () => {
|
|||
const container = useRef<HTMLDivElement>(null!)
|
||||
const [itemKey, setItemKey] = useState('')
|
||||
const hasModals = useSnapshot(activeModalStack).length
|
||||
const { currentTouch, appConfig } = useSnapshot(miscUiState)
|
||||
const mobileOpenInventory = currentTouch && !appConfig?.disabledCommands?.includes('general.inventory')
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController()
|
||||
|
|
@ -105,7 +107,7 @@ const HotbarInner = () => {
|
|||
canvasManager.setScale(currentScaling.scale)
|
||||
|
||||
canvasManager.windowHeight = 25 * canvasManager.scale
|
||||
canvasManager.windowWidth = (210 - (inv.inventory.supportsOffhand ? 0 : 25) + (miscUiState.currentTouch ? 28 : 0)) * canvasManager.scale
|
||||
canvasManager.windowWidth = (210 - (inv.inventory.supportsOffhand ? 0 : 25) + (mobileOpenInventory ? 28 : 0)) * canvasManager.scale
|
||||
}
|
||||
setSize()
|
||||
watchUnloadForCleanup(subscribe(currentScaling, setSize))
|
||||
|
|
@ -119,8 +121,9 @@ const HotbarInner = () => {
|
|||
canvasManager.canvas.onclick = (e) => {
|
||||
if (!isGameActive(true)) return
|
||||
const pos = inv.canvasManager.getMousePos(inv.canvas, e)
|
||||
if (canvasManager.canvas.width - pos.x < 35 * inv.canvasManager.scale) {
|
||||
openPlayerInventory()
|
||||
if (canvasManager.canvas.width - pos.x < 35 * inv.canvasManager.scale && mobileOpenInventory) {
|
||||
triggerCommand('general.inventory', true)
|
||||
triggerCommand('general.inventory', false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -180,17 +183,8 @@ const HotbarInner = () => {
|
|||
})
|
||||
document.addEventListener('touchend', (e) => {
|
||||
if (touchStart && (e.target as HTMLElement).closest('.hotbar') && Date.now() - touchStart > 700) {
|
||||
// drop item
|
||||
bot._client.write('block_dig', {
|
||||
'status': 4,
|
||||
'location': {
|
||||
'x': 0,
|
||||
'z': 0,
|
||||
'y': 0
|
||||
},
|
||||
'face': 0,
|
||||
sequence: 0
|
||||
})
|
||||
triggerCommand('general.dropStack', true)
|
||||
triggerCommand('general.dropStack', false)
|
||||
}
|
||||
touchStart = 0
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { useRef, useState, useMemo } from 'react'
|
||||
import { GameMode } from 'mineflayer'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { options } from '../optionsStorage'
|
||||
import { armor } from './armorValues'
|
||||
import HealthBar from './HealthBar'
|
||||
import FoodBar from './FoodBar'
|
||||
|
|
@ -8,6 +10,8 @@ import BreathBar from './BreathBar'
|
|||
import './HealthBar.css'
|
||||
|
||||
export default () => {
|
||||
const { disabledUiParts } = useSnapshot(options)
|
||||
|
||||
const [damaged, setDamaged] = useState(false)
|
||||
const [healthValue, setHealthValue] = useState(bot.health)
|
||||
const [food, setFood] = useState(bot.food)
|
||||
|
|
@ -91,7 +95,7 @@ export default () => {
|
|||
}, [])
|
||||
|
||||
return <div className='hud-bars-container'>
|
||||
<HealthBar
|
||||
{!disabledUiParts.includes('health-bar') && <HealthBar
|
||||
gameMode={gameMode}
|
||||
isHardcore={isHardcore}
|
||||
damaged={damaged}
|
||||
|
|
@ -102,12 +106,12 @@ export default () => {
|
|||
setEffectToAdd(null)
|
||||
setEffectToRemove(null)
|
||||
}}
|
||||
/>
|
||||
<ArmorBar
|
||||
/>}
|
||||
{!disabledUiParts.includes('armor-bar') && <ArmorBar
|
||||
armorValue={armorValue}
|
||||
style={gameMode !== 'survival' && gameMode !== 'adventure' ? { display: 'none' } : { display: 'flex' }}
|
||||
/>
|
||||
<FoodBar
|
||||
/>}
|
||||
{!disabledUiParts.includes('food-bar') && <FoodBar
|
||||
gameMode={gameMode}
|
||||
food={food}
|
||||
effectToAdd={effectToAdd}
|
||||
|
|
@ -116,9 +120,9 @@ export default () => {
|
|||
setEffectToAdd(null)
|
||||
setEffectToRemove(null)
|
||||
}}
|
||||
/>
|
||||
<BreathBar
|
||||
/>}
|
||||
{!disabledUiParts.includes('breath-bar') && <BreathBar
|
||||
oxygen={gameMode !== 'survival' && gameMode !== 'adventure' ? 0 : oxygen}
|
||||
/>
|
||||
/>}
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import Screen from './Screen'
|
|||
import styles from './PauseScreen.module.css'
|
||||
import { DiscordButton } from './DiscordButton'
|
||||
import { showNotification } from './NotificationProvider'
|
||||
import { appStatusState, reconnectReload } from './AppStatusProvider'
|
||||
import { appStatusState, lastConnectOptions, reconnectReload } from './AppStatusProvider'
|
||||
import NetworkStatus from './NetworkStatus'
|
||||
import PauseLinkButtons from './PauseLinkButtons'
|
||||
import { pixelartIcons } from './PixelartIcon'
|
||||
|
|
@ -163,6 +163,7 @@ export default () => {
|
|||
const { noConnection } = useSnapshot(gameAdditionalState)
|
||||
const { active: packetsReplaceActive, hasRecordedPackets: packetsReplaceHasRecordedPackets } = useSnapshot(packetsRecordingState)
|
||||
const { displayRecordButton: displayPacketsButtons } = useSnapshot(options)
|
||||
const { appConfig } = useSnapshot(miscUiState)
|
||||
|
||||
const handlePointerLockChange = () => {
|
||||
if (!pointerLock.hasPointerLock && activeModalStack.length === 0) {
|
||||
|
|
@ -264,7 +265,7 @@ export default () => {
|
|||
<div className={styles.pause_container}>
|
||||
<Button className="button" style={{ width: '204px' }} onClick={onReturnPress}>Back to Game</Button>
|
||||
<PauseLinkButtons />
|
||||
<Button className="button" style={{ width: '204px' }} onClick={() => openOptionsMenu('main')}>Options</Button>
|
||||
<Button className="button" style={{ width: '204px' }} onClick={() => openOptionsMenu('main')}>Options...</Button>
|
||||
{singleplayer ? (
|
||||
<div className={styles.row}>
|
||||
<Button className="button" style={{ width: '170px' }} onClick={async () => clickJoinLinkButton()}>
|
||||
|
|
@ -293,10 +294,54 @@ export default () => {
|
|||
{fsState.inMemorySave && !fsState.syncFs && !fsState.isReadonly ? 'Save & Quit' : 'Disconnect & Reset'}
|
||||
</Button>
|
||||
</>}
|
||||
{noConnection && (
|
||||
<Button className="button" style={{ width: '204px' }} onClick={reconnectReload}>
|
||||
Reconnect
|
||||
</Button>
|
||||
{(noConnection || appConfig?.alwaysReconnectButton) && (
|
||||
<div className={styles.row}>
|
||||
<Button className="button" style={{ width: appConfig?.reportBugButtonWithReconnect ? '98px' : '204px' }} onClick={reconnectReload}>
|
||||
Reconnect
|
||||
</Button>
|
||||
{appConfig?.reportBugButtonWithReconnect && (
|
||||
<Button
|
||||
label="Report Problem"
|
||||
className="button"
|
||||
style={{ width: '98px' }}
|
||||
onClick={async () => {
|
||||
const platform = (navigator as any).userAgentData?.platform ?? navigator.platform
|
||||
const body = `Version: ${window.location.hostname}\nServer: ${lastConnectOptions.value?.server ?? '<not a server>'}\nPlatform: ${platform}\nWebsite: ${window.location.href}`
|
||||
const currentHost = window.location.hostname
|
||||
const options = [
|
||||
'GitHub (please use it if you can)',
|
||||
'Email',
|
||||
...((currentHost === 'mcraft.fun' || currentHost === 'ru.mcraft.fun') ? ['Try Beta Version'] : []),
|
||||
// 'Use previous versions of client'
|
||||
]
|
||||
const action = await showOptionsModal('Report client issue', options)
|
||||
if (!action) return
|
||||
|
||||
switch (action) {
|
||||
case 'GitHub (please use it if you can)':
|
||||
openGithub(`/issues/new?body=${encodeURIComponent(body)}&title=${encodeURIComponent('[Bug Report] <describe your issue here>')}&labels=bug`)
|
||||
break
|
||||
case 'Email': {
|
||||
window.location.href = `mailto:support@mcraft.fun?body=${encodeURIComponent(body)}`
|
||||
break
|
||||
}
|
||||
case 'Try Beta Version': {
|
||||
if (currentHost === 'mcraft.fun') {
|
||||
window.location.href = 'https://s.mcraft.fun'
|
||||
} else if (currentHost === 'ru.mcraft.fun') {
|
||||
window.location.href = 'https://s.pcm.gg'
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'Use previous versions of client':
|
||||
// TODO: Implement versions screen
|
||||
void showOptionsModal('Previous versions', [])
|
||||
break
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<LoadingTimer />
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import PlayerListOverlay from './PlayerListOverlay'
|
|||
import './PlayerListOverlay.css'
|
||||
import { lastConnectOptions } from './AppStatusProvider'
|
||||
|
||||
const MAX_COLUMNS = 4
|
||||
const MAX_ROWS_PER_COL = 10
|
||||
|
||||
type Players = typeof bot.players
|
||||
|
|
@ -56,21 +57,24 @@ export default () => {
|
|||
}
|
||||
}, [serverIp])
|
||||
|
||||
|
||||
const playersArray = Object.values(players).sort((a, b) => {
|
||||
if (a.username > b.username) return 1
|
||||
if (a.username < b.username) return -1
|
||||
return 0
|
||||
})
|
||||
|
||||
// Calculate optimal column distribution
|
||||
const totalPlayers = playersArray.length
|
||||
const numColumns = Math.min(MAX_COLUMNS, Math.ceil(totalPlayers / MAX_ROWS_PER_COL))
|
||||
const playersPerColumn = Math.ceil(totalPlayers / numColumns)
|
||||
|
||||
const lists = [] as Array<typeof playersArray>
|
||||
|
||||
let tempList = [] as typeof playersArray
|
||||
for (let i = 0; i < playersArray.length; i++) {
|
||||
tempList.push(playersArray[i])
|
||||
|
||||
if ((i + 1) % MAX_ROWS_PER_COL === 0 || i + 1 === playersArray.length) {
|
||||
lists.push([...tempList])
|
||||
tempList = []
|
||||
for (let i = 0; i < numColumns; i++) {
|
||||
const startIdx = i * playersPerColumn
|
||||
const endIdx = Math.min(startIdx + playersPerColumn, totalPlayers)
|
||||
if (startIdx < totalPlayers) {
|
||||
lists.push(playersArray.slice(startIdx, endIdx))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,10 +16,15 @@ export default () => {
|
|||
|
||||
if (!isModalActive/* || conflicts.length === 0 */) return null
|
||||
|
||||
const clampText = (text: string) => {
|
||||
if (typeof text !== 'string') text = JSON.stringify(text)
|
||||
return text.length > 30 ? text.slice(0, 30) + '...' : text
|
||||
}
|
||||
|
||||
const conflictText = conflicts.map(conflict => {
|
||||
const localTime = formatTimestamp(conflict.localStorageTimestamp)
|
||||
const cookieTime = formatTimestamp(conflict.cookieTimestamp)
|
||||
return `${conflict.key}: LocalStorage (${localTime}) vs Cookie (${cookieTime})`
|
||||
return `${conflict.key}: LocalStorage (${localTime}, ${clampText(conflict.localStorageValue)}) vs Cookie (${cookieTime}, ${clampText(conflict.cookieValue)})`
|
||||
}).join('\n')
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import type { BaseServerInfo } from './AddServerOrConnect'
|
|||
|
||||
// when opening html file locally in browser, localStorage is shared between all ever opened html files, so we try to avoid conflicts
|
||||
const localStoragePrefix = process.env?.SINGLE_FILE_BUILD ? 'minecraft-web-client:' : ''
|
||||
const cookiePrefix = ''
|
||||
const cookiePrefix = process.env.COOKIE_STORAGE_PREFIX || ''
|
||||
const { localStorage } = window
|
||||
const migrateRemoveLocalStorage = false
|
||||
|
||||
|
|
@ -134,12 +134,19 @@ const detectStorageConflicts = (): StorageConflict[] => {
|
|||
delete localData.timestamp
|
||||
delete cookieData.timestamp
|
||||
|
||||
if (JSON.stringify(localData) !== JSON.stringify(cookieData)) {
|
||||
const isDataEmpty = (data: any) => {
|
||||
if (typeof data === 'object' && data !== null) {
|
||||
return Object.keys(data).length === 0
|
||||
}
|
||||
return !data && data !== 0 && data !== false
|
||||
}
|
||||
|
||||
if (JSON.stringify(localData) !== JSON.stringify(cookieData) && !isDataEmpty(localData) && !isDataEmpty(cookieData)) {
|
||||
conflicts.push({
|
||||
key,
|
||||
localStorageValue: localData,
|
||||
localStorageTimestamp: localTimestamp,
|
||||
cookieValue: cookieData,
|
||||
cookieValue: (typeof cookieData === 'object' && cookieData !== null && 'data' in cookieData) ? cookieData.data : cookieData,
|
||||
cookieTimestamp
|
||||
})
|
||||
}
|
||||
|
|
@ -224,9 +231,10 @@ export const appStorage = proxy({ ...defaultStorageData })
|
|||
|
||||
// Check if cookie storage should be used (will be set by options)
|
||||
const shouldUseCookieStorage = () => {
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
|
||||
const isSecureCookiesAvailable = () => {
|
||||
// either https or localhost
|
||||
return window.location.protocol === 'https:' || window.location.hostname === 'localhost'
|
||||
return window.location.protocol === 'https:' || (window.location.hostname === 'localhost' && !isSafari)
|
||||
}
|
||||
if (!isSecureCookiesAvailable()) {
|
||||
return false
|
||||
|
|
@ -395,6 +403,12 @@ export const resolveStorageConflicts = (useLocalStorage: boolean) => {
|
|||
}
|
||||
}
|
||||
|
||||
// forcefully set data again
|
||||
for (const conflict of storageConflicts) {
|
||||
appStorage[conflict.key] = useLocalStorage ? conflict.localStorageValue : conflict.cookieValue
|
||||
saveKey(conflict.key as keyof StorageData)
|
||||
}
|
||||
|
||||
// Clear conflicts and restore data
|
||||
storageConflicts = []
|
||||
restoreStorageData()
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ subscribeKey(miscUiState, 'gameLoaded', async () => {
|
|||
}
|
||||
|
||||
const musicStartCheck = async (force = false) => {
|
||||
if (!soundMap) return
|
||||
if (!soundMap || !bot) return
|
||||
// 20% chance to start music
|
||||
if (Math.random() > 0.2 && !force && !options.enableMusic) return
|
||||
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ export const reloadChunks = async () => {
|
|||
}
|
||||
|
||||
export const openGithub = (addUrl = '') => {
|
||||
window.open(`${process.env.GITHUB_URL}${addUrl}`, '_blank')
|
||||
window.open(`${process.env.GITHUB_URL?.replace(/\/$/, '')}${addUrl}`, '_blank')
|
||||
}
|
||||
|
||||
export const resolveTimeout = async (promise, timeout = 10_000) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue