hotfixes (#115)
This commit is contained in:
commit
a87f6d3fa3
21 changed files with 443 additions and 75 deletions
|
|
@ -114,6 +114,9 @@ Press `Y` to set query parameters to url of your current game state.
|
|||
- `?singleplayer=1` - Create empty world on load. Nothing will be saved
|
||||
- `?noSave=true` - Disable auto save on unload / disconnect / export. Only manual save with `/save` command will work
|
||||
|
||||
- `?map=<map_url>` - Load the map from ZIP. You can use any url, but it must be CORS enabled.
|
||||
- `?setting=<setting_name>:<setting_value>` - Set the and lock the setting on load. You can set multiple settings by separating them with `&` e.g. `?setting=autoParkour:true&setting=renderDistance:4`
|
||||
|
||||
### Notable Things that Power this Project
|
||||
|
||||
- [Mineflayer](https://github.com/PrismarineJS/mineflayer) - Handles all client-side communications with the server (including the builtin one) - forked
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
"promoteServers": [
|
||||
{
|
||||
"ip": "kaboom.pw",
|
||||
"version": "1.18.2",
|
||||
"description": "Chaos and destruction server. Free for everyone."
|
||||
},
|
||||
{
|
||||
|
|
@ -15,6 +16,7 @@
|
|||
},
|
||||
{
|
||||
"ip": "play.minemalia.com",
|
||||
"version": "1.18.2",
|
||||
"description": "Only login with existing accounts."
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ fs.writeFileSync('dist/index.html', fs.readFileSync('index.html', 'utf8').replac
|
|||
|
||||
const watch = process.argv.includes('--watch') || process.argv.includes('-w')
|
||||
const prod = process.argv.includes('--prod')
|
||||
if (prod) process.env.PROD = 'true'
|
||||
const dev = !prod
|
||||
|
||||
const banner = [
|
||||
|
|
|
|||
16
package.json
16
package.json
|
|
@ -62,6 +62,7 @@
|
|||
"esbuild": "^0.19.3",
|
||||
"esbuild-plugin-polyfill-node": "^0.3.0",
|
||||
"express": "^4.18.2",
|
||||
"filesize": "^10.0.12",
|
||||
"flying-squid": "npm:@zardoy/flying-squid@^0.0.20",
|
||||
"fs-extra": "^11.1.1",
|
||||
"google-drive-browserfs": "github:zardoy/browserfs#google-drive",
|
||||
|
|
@ -70,6 +71,8 @@
|
|||
"lodash-es": "^4.17.21",
|
||||
"minecraft-assets": "^1.12.2",
|
||||
"minecraft-data": "3.65.0",
|
||||
"minecraft-protocol": "github:PrismarineJS/node-minecraft-protocol",
|
||||
"mojangson": "^2.0.4",
|
||||
"net-browserify": "github:zardoy/prismarinejs-net-browserify",
|
||||
"node-gzip": "^1.1.2",
|
||||
"peerjs": "^1.5.0",
|
||||
|
|
@ -77,6 +80,7 @@
|
|||
"prismarine-provider-anvil": "github:zardoy/prismarine-provider-anvil#everything",
|
||||
"prosemirror-example-setup": "^1.2.2",
|
||||
"prosemirror-markdown": "^1.12.0",
|
||||
"prosemirror-menu": "^1.2.4",
|
||||
"prosemirror-state": "^1.4.3",
|
||||
"prosemirror-view": "^1.33.1",
|
||||
"qrcode.react": "^3.1.0",
|
||||
|
|
@ -84,18 +88,15 @@
|
|||
"react-dom": "^18.2.0",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"remark": "^15.0.1",
|
||||
"filesize": "^10.0.12",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"skinview3d": "^3.0.1",
|
||||
"source-map-js": "^1.0.2",
|
||||
"stats-gl": "^1.0.5",
|
||||
"stats.js": "^0.17.0",
|
||||
"use-typed-event-listener": "^4.0.2",
|
||||
"mojangson": "^2.0.4",
|
||||
"prosemirror-menu": "^1.2.4",
|
||||
"tabbable": "^6.2.0",
|
||||
"title-case": "3.x",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
"use-typed-event-listener": "^4.0.2",
|
||||
"valtio": "^1.11.1",
|
||||
"vec3": "^0.1.7",
|
||||
"workbox-build": "^7.0.0"
|
||||
|
|
@ -116,7 +117,7 @@
|
|||
"browserify-zlib": "^0.2.0",
|
||||
"buffer": "^6.0.3",
|
||||
"constants-browserify": "^1.0.0",
|
||||
"contro-max": "^0.1.2",
|
||||
"contro-max": "^0.1.6",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"cypress": "^10.11.0",
|
||||
"cypress-esbuild-preprocessor": "^1.0.2",
|
||||
|
|
@ -156,12 +157,15 @@
|
|||
"prismarine-world": "github:zardoy/prismarine-world#next-era",
|
||||
"minecraft-data": "3.65.0",
|
||||
"prismarine-provider-anvil": "github:zardoy/prismarine-provider-anvil#everything",
|
||||
"minecraft-protocol": "github:zardoy/minecraft-protocol#everything",
|
||||
"minecraft-protocol": "github:PrismarineJS/node-minecraft-protocol",
|
||||
"react": "^18.2.0",
|
||||
"prismarine-chunk": "github:zardoy/prismarine-chunk"
|
||||
},
|
||||
"updateConfig": {
|
||||
"ignoreDependencies": []
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"minecraft-protocol@1.47.0": "patches/minecraft-protocol@1.47.0.patch"
|
||||
}
|
||||
},
|
||||
"packageManager": "pnpm@9.0.4"
|
||||
|
|
|
|||
130
patches/minecraft-protocol@1.47.0.patch
Normal file
130
patches/minecraft-protocol@1.47.0.patch
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
diff --git a/src/client/autoVersion.js b/src/client/autoVersion.js
|
||||
index c437ecf3a0e4ab5758a48538c714b7e9651bb5da..d9c9895ae8614550aa09ad60a396ac32ffdf1287 100644
|
||||
--- a/src/client/autoVersion.js
|
||||
+++ b/src/client/autoVersion.js
|
||||
@@ -9,7 +9,7 @@ module.exports = function (client, options) {
|
||||
client.wait_connect = true // don't let src/client/setProtocol proceed on socket 'connect' until 'connect_allowed'
|
||||
debug('pinging', options.host)
|
||||
// TODO: use 0xfe ping instead for better compatibility/performance? https://github.com/deathcap/node-minecraft-ping
|
||||
- ping(options, function (err, response) {
|
||||
+ ping(options, async function (err, response) {
|
||||
if (err) { return client.emit('error', err) }
|
||||
debug('ping response', response)
|
||||
// TODO: could also use ping pre-connect to save description, type, max players, etc.
|
||||
@@ -40,6 +40,7 @@ module.exports = function (client, options) {
|
||||
|
||||
// Reinitialize client object with new version TODO: move out of its constructor?
|
||||
client.version = minecraftVersion
|
||||
+ await options.versionSelectedHook?.(client)
|
||||
client.state = states.HANDSHAKING
|
||||
|
||||
// Let other plugins such as Forge/FML (modinfo) respond to the ping response
|
||||
diff --git a/src/client/encrypt.js b/src/client/encrypt.js
|
||||
index b9d21bab9faccd5dbf1975fc423fc55c73e906c5..99ffd76527b410e3a393181beb260108f4c63536 100644
|
||||
--- a/src/client/encrypt.js
|
||||
+++ b/src/client/encrypt.js
|
||||
@@ -25,7 +25,11 @@ module.exports = function (client, options) {
|
||||
if (packet.serverId !== '-') {
|
||||
debug('This server appears to be an online server and you are providing no password, the authentication will probably fail')
|
||||
}
|
||||
- sendEncryptionKeyResponse()
|
||||
+ client.end('This server appears to be an online server and you are providing no authentication. Try authenticating first.')
|
||||
+ // sendEncryptionKeyResponse()
|
||||
+ // client.once('set_compression', () => {
|
||||
+ // clearTimeout(loginTimeout)
|
||||
+ // })
|
||||
}
|
||||
|
||||
function onJoinServerResponse (err) {
|
||||
diff --git a/src/client.js b/src/client.js
|
||||
index c89375e32babbf3559655b1e95f6441b9a30796f..f24cd5dc8fa9a0a4000b184fb3c79590a3ad8b8a 100644
|
||||
--- a/src/client.js
|
||||
+++ b/src/client.js
|
||||
@@ -88,10 +88,12 @@ class Client extends EventEmitter {
|
||||
parsed.metadata.name = parsed.data.name
|
||||
parsed.data = parsed.data.params
|
||||
parsed.metadata.state = state
|
||||
- debug('read packet ' + state + '.' + parsed.metadata.name)
|
||||
- if (debug.enabled) {
|
||||
- const s = JSON.stringify(parsed.data, null, 2)
|
||||
- debug(s && s.length > 10000 ? parsed.data : s)
|
||||
+ if (!globalThis.excludeCommunicationDebugEvents?.includes(parsed.metadata.name)) {
|
||||
+ debug('read packet ' + state + '.' + parsed.metadata.name)
|
||||
+ if (debug.enabled) {
|
||||
+ const s = JSON.stringify(parsed.data, null, 2)
|
||||
+ debug(s && s.length > 10000 ? parsed.data : s)
|
||||
+ }
|
||||
}
|
||||
if (this._hasBundlePacket && parsed.metadata.name === 'bundle_delimiter') {
|
||||
if (this._mcBundle.length) { // End bundle
|
||||
@@ -109,7 +111,13 @@ class Client extends EventEmitter {
|
||||
this._hasBundlePacket = false
|
||||
}
|
||||
} else {
|
||||
- emitPacket(parsed)
|
||||
+ try {
|
||||
+ emitPacket(parsed)
|
||||
+ } catch (err) {
|
||||
+ console.log('Client incorrectly handled packet ' + parsed.metadata.name)
|
||||
+ console.error(err)
|
||||
+ // todo investigate why it doesn't close the stream even if unhandled there
|
||||
+ }
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -166,7 +174,10 @@ class Client extends EventEmitter {
|
||||
}
|
||||
|
||||
const onFatalError = (err) => {
|
||||
- this.emit('error', err)
|
||||
+ // todo find out what is trying to write after client disconnect
|
||||
+ if(err.code !== 'ECONNABORTED') {
|
||||
+ this.emit('error', err)
|
||||
+ }
|
||||
endSocket()
|
||||
}
|
||||
|
||||
@@ -195,6 +206,8 @@ class Client extends EventEmitter {
|
||||
serializer -> framer -> socket -> splitter -> deserializer */
|
||||
if (this.serializer) {
|
||||
this.serializer.end()
|
||||
+ this.socket?.end()
|
||||
+ this.socket?.emit('end')
|
||||
} else {
|
||||
if (this.socket) this.socket.end()
|
||||
}
|
||||
@@ -236,8 +249,11 @@ class Client extends EventEmitter {
|
||||
|
||||
write (name, params) {
|
||||
if (!this.serializer.writable) { return }
|
||||
- debug('writing packet ' + this.state + '.' + name)
|
||||
- debug(params)
|
||||
+ if (!globalThis.excludeCommunicationDebugEvents?.includes(name)) {
|
||||
+ debug(`[${this.state}] from ${this.isServer ? 'server' : 'client'}: ` + name)
|
||||
+ debug(params)
|
||||
+ }
|
||||
+ this.emit('writePacket', name, params)
|
||||
this.serializer.write({ name, params })
|
||||
}
|
||||
|
||||
diff --git a/src/index.d.ts b/src/index.d.ts
|
||||
index 0a5821c32d735e11205a280aa5a503c13533dc14..94a49f661d922478b940d853169b6087e6ec3df5 100644
|
||||
--- a/src/index.d.ts
|
||||
+++ b/src/index.d.ts
|
||||
@@ -121,6 +121,7 @@ declare module 'minecraft-protocol' {
|
||||
sessionServer?: string
|
||||
keepAlive?: boolean
|
||||
closeTimeout?: number
|
||||
+ closeTimeout?: number
|
||||
noPongTimeout?: number
|
||||
checkTimeoutInterval?: number
|
||||
version?: string
|
||||
@@ -141,6 +142,8 @@ declare module 'minecraft-protocol' {
|
||||
disableChatSigning?: boolean
|
||||
/** Pass custom client implementation if needed. */
|
||||
Client?: Client
|
||||
+ /** Can be used to prepare mc data on autoVersion (client.version has selected version) */
|
||||
+ versionSelectedHook?: (client: Client) => Promise<void> | void
|
||||
}
|
||||
|
||||
export class Server extends EventEmitter {
|
||||
82
pnpm-lock.yaml
generated
82
pnpm-lock.yaml
generated
|
|
@ -12,10 +12,15 @@ overrides:
|
|||
prismarine-world: github:zardoy/prismarine-world#next-era
|
||||
minecraft-data: 3.65.0
|
||||
prismarine-provider-anvil: github:zardoy/prismarine-provider-anvil#everything
|
||||
minecraft-protocol: github:zardoy/minecraft-protocol#everything
|
||||
minecraft-protocol: github:PrismarineJS/node-minecraft-protocol
|
||||
react: ^18.2.0
|
||||
prismarine-chunk: github:zardoy/prismarine-chunk
|
||||
|
||||
patchedDependencies:
|
||||
minecraft-protocol@1.47.0:
|
||||
hash: 2uxevyasyasdavsxuehfavgkjq
|
||||
path: patches/minecraft-protocol@1.47.0.patch
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
|
|
@ -122,6 +127,9 @@ importers:
|
|||
minecraft-data:
|
||||
specifier: 3.65.0
|
||||
version: 3.65.0
|
||||
minecraft-protocol:
|
||||
specifier: github:PrismarineJS/node-minecraft-protocol
|
||||
version: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/ccab9fb39681f3ebe0d264e2a3f833aa3c5a1ac7(patch_hash=2uxevyasyasdavsxuehfavgkjq)(encoding@0.1.13)
|
||||
mojangson:
|
||||
specifier: ^2.0.4
|
||||
version: 2.0.4
|
||||
|
|
@ -257,8 +265,8 @@ importers:
|
|||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
contro-max:
|
||||
specifier: ^0.1.2
|
||||
version: 0.1.2(typescript@5.5.0-beta)
|
||||
specifier: ^0.1.6
|
||||
version: 0.1.6(typescript@5.5.0-beta)
|
||||
crypto-browserify:
|
||||
specifier: ^3.12.0
|
||||
version: 3.12.0
|
||||
|
|
@ -3800,8 +3808,8 @@ packages:
|
|||
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
contro-max@0.1.2:
|
||||
resolution: {integrity: sha512-mY9aRQ9on/iyzvyhb4OD/10WRRKulVd92F7cxMFVn3rq5EwI+gZitGpHN2mp9+IzwRgBJrOKr1C051b3YlEktQ==}
|
||||
contro-max@0.1.6:
|
||||
resolution: {integrity: sha512-QsoOcAlbtNgkCGBvwKsh+GUVZ2c5zfMgYQCu+v4MplX5VolkWhMwAcEOBRxt8oENbnRXOKUGQr816Ey1G4/jpg==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
convert-source-map@1.9.0:
|
||||
|
|
@ -4190,10 +4198,6 @@ packages:
|
|||
resolution: {integrity: sha512-y5JHnrygHnCndtqVHHDhCr0ZzzWHK5RBTczWRlGSIR5UnGHBXuxpoaE0UB5E82qym8ma2dI799wDSSJN2e4VSg==}
|
||||
engines: {node: '>=5'}
|
||||
|
||||
emittery@0.10.2:
|
||||
resolution: {integrity: sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
|
||||
|
|
@ -6038,9 +6042,13 @@ packages:
|
|||
resolution: {tarball: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/5554c7ab0a74bce52aa5f5f04a48eb8d3b9ac65c}
|
||||
version: 1.0.1
|
||||
|
||||
minecraft-protocol@https://codeload.github.com/zardoy/minecraft-protocol/tar.gz/2c14a686bfe7cbd9a5c87b629b402295ee86219f:
|
||||
resolution: {tarball: https://codeload.github.com/zardoy/minecraft-protocol/tar.gz/2c14a686bfe7cbd9a5c87b629b402295ee86219f}
|
||||
version: 1.45.0
|
||||
minecraft-protocol@1.47.0:
|
||||
resolution: {integrity: sha512-IHL8faXLLIWv1O+2v2NgyKlooilu/OiSL9orI8Kqed/rZvVOrFPzs2PwMAYjpQX9gxLPhiSU19KqZ8CjfNuqhg==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/ccab9fb39681f3ebe0d264e2a3f833aa3c5a1ac7:
|
||||
resolution: {tarball: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/ccab9fb39681f3ebe0d264e2a3f833aa3c5a1ac7}
|
||||
version: 1.47.0
|
||||
engines: {node: '>=14'}
|
||||
|
||||
minecraft-wrap@1.5.1:
|
||||
|
|
@ -6698,6 +6706,9 @@ packages:
|
|||
resolution: {tarball: https://codeload.github.com/zardoy/prismarine-block/tar.gz/ada4ec3fdfbbc1cc20ab01d0e23f0718a77cc1a0}
|
||||
version: 1.17.1
|
||||
|
||||
prismarine-chat@1.10.1:
|
||||
resolution: {integrity: sha512-XukYcuueuhDxzEXG7r8BZyt6jOObrPPB4JESCgb+/XenB9nExoSHF8eTQWWj8faKPLqm1dRQaYwFJlNBlJZJUw==}
|
||||
|
||||
prismarine-chat@1.9.1:
|
||||
resolution: {integrity: sha512-x7WWa5MNhiLZSO6tw+YyKpzquFZ+DNISVgiV6K3SU0GsishMXe+nto02WhF/4AuFerKdugm9u1d/r4C4zSkJOg==}
|
||||
|
||||
|
|
@ -11924,7 +11935,7 @@ snapshots:
|
|||
flatmap: 0.0.3
|
||||
long: 5.2.3
|
||||
minecraft-data: 3.65.0
|
||||
minecraft-protocol: https://codeload.github.com/zardoy/minecraft-protocol/tar.gz/2c14a686bfe7cbd9a5c87b629b402295ee86219f(encoding@0.1.13)
|
||||
minecraft-protocol: 1.47.0(patch_hash=2uxevyasyasdavsxuehfavgkjq)(encoding@0.1.13)
|
||||
mkdirp: 2.1.6
|
||||
node-gzip: 1.1.2
|
||||
node-rsa: 1.1.1
|
||||
|
|
@ -12829,10 +12840,11 @@ snapshots:
|
|||
|
||||
content-type@1.0.5: {}
|
||||
|
||||
contro-max@0.1.2(typescript@5.5.0-beta):
|
||||
contro-max@0.1.6(typescript@5.5.0-beta):
|
||||
dependencies:
|
||||
emittery: 0.10.2
|
||||
events: 3.3.0
|
||||
lodash-es: 4.17.21
|
||||
typed-emitter: 2.1.0
|
||||
optionalDependencies:
|
||||
react: 18.2.0
|
||||
use-typed-event-listener: 4.0.2(react@18.2.0)(typescript@5.5.0-beta)
|
||||
|
|
@ -13306,8 +13318,6 @@ snapshots:
|
|||
|
||||
emit-then@2.0.0: {}
|
||||
|
||||
emittery@0.10.2: {}
|
||||
|
||||
emoji-regex@8.0.0: {}
|
||||
|
||||
emoji-regex@9.2.2: {}
|
||||
|
|
@ -15670,7 +15680,7 @@ snapshots:
|
|||
- '@types/react'
|
||||
- react
|
||||
|
||||
minecraft-protocol@https://codeload.github.com/zardoy/minecraft-protocol/tar.gz/2c14a686bfe7cbd9a5c87b629b402295ee86219f(encoding@0.1.13):
|
||||
minecraft-protocol@1.47.0(patch_hash=2uxevyasyasdavsxuehfavgkjq)(encoding@0.1.13):
|
||||
dependencies:
|
||||
'@types/readable-stream': 4.0.12
|
||||
aes-js: 3.1.2
|
||||
|
|
@ -15684,6 +15694,32 @@ snapshots:
|
|||
node-fetch: 2.7.0(encoding@0.1.13)
|
||||
node-rsa: 0.4.2
|
||||
prismarine-auth: 2.4.2(encoding@0.1.13)
|
||||
prismarine-chat: 1.10.1
|
||||
prismarine-nbt: 2.5.0
|
||||
prismarine-realms: 1.3.2(encoding@0.1.13)
|
||||
protodef: 1.15.0
|
||||
readable-stream: 4.5.2
|
||||
uuid-1345: 1.0.2
|
||||
yggdrasil: 1.7.0(encoding@0.1.13)
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/ccab9fb39681f3ebe0d264e2a3f833aa3c5a1ac7(patch_hash=2uxevyasyasdavsxuehfavgkjq)(encoding@0.1.13):
|
||||
dependencies:
|
||||
'@types/readable-stream': 4.0.12
|
||||
aes-js: 3.1.2
|
||||
buffer-equal: 1.0.1
|
||||
debug: 4.3.4(supports-color@8.1.1)
|
||||
endian-toggle: 0.0.0
|
||||
lodash.get: 4.4.2
|
||||
lodash.merge: 4.6.2
|
||||
minecraft-data: 3.65.0
|
||||
minecraft-folder-path: 1.2.0
|
||||
node-fetch: 2.7.0(encoding@0.1.13)
|
||||
node-rsa: 0.4.2
|
||||
prismarine-auth: 2.4.2(encoding@0.1.13)
|
||||
prismarine-chat: 1.10.1
|
||||
prismarine-nbt: 2.5.0
|
||||
prismarine-realms: 1.3.2(encoding@0.1.13)
|
||||
protodef: 1.15.0
|
||||
|
|
@ -15730,7 +15766,7 @@ snapshots:
|
|||
mineflayer@https://codeload.github.com/PrismarineJS/mineflayer/tar.gz/5a544cf2547a6e0f1f17786962d77a33c661c02f(encoding@0.1.13):
|
||||
dependencies:
|
||||
minecraft-data: 3.65.0
|
||||
minecraft-protocol: https://codeload.github.com/zardoy/minecraft-protocol/tar.gz/2c14a686bfe7cbd9a5c87b629b402295ee86219f(encoding@0.1.13)
|
||||
minecraft-protocol: 1.47.0(patch_hash=2uxevyasyasdavsxuehfavgkjq)(encoding@0.1.13)
|
||||
prismarine-biome: 1.3.0(minecraft-data@3.65.0)(prismarine-registry@1.7.0)
|
||||
prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/ada4ec3fdfbbc1cc20ab01d0e23f0718a77cc1a0
|
||||
prismarine-chat: 1.9.1
|
||||
|
|
@ -16445,6 +16481,12 @@ snapshots:
|
|||
prismarine-nbt: 2.5.0
|
||||
prismarine-registry: 1.7.0
|
||||
|
||||
prismarine-chat@1.10.1:
|
||||
dependencies:
|
||||
mojangson: 2.0.4
|
||||
prismarine-nbt: 2.5.0
|
||||
prismarine-registry: 1.7.0
|
||||
|
||||
prismarine-chat@1.9.1:
|
||||
dependencies:
|
||||
mojangson: 2.0.4
|
||||
|
|
@ -16467,7 +16509,7 @@ snapshots:
|
|||
|
||||
prismarine-entity@2.3.1:
|
||||
dependencies:
|
||||
prismarine-chat: 1.9.1
|
||||
prismarine-chat: 1.10.1
|
||||
prismarine-item: 1.14.0
|
||||
prismarine-registry: 1.7.0
|
||||
vec3: 0.1.8
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ const plugins = [
|
|||
//@ts-ignore
|
||||
for (const file of outputFiles) {
|
||||
let contents = file.text
|
||||
if (file.path.endsWith('.map') && file.text) {
|
||||
if (file.path.endsWith('.map') && file.text && !process.env.PROD) {
|
||||
const map = JSON.parse(file.text)
|
||||
removeNodeModulesSourcemaps(map)
|
||||
contents = JSON.stringify(map)
|
||||
|
|
|
|||
126
src/controls.ts
126
src/controls.ts
|
|
@ -15,6 +15,7 @@ import { fsState } from './loadSave'
|
|||
import { showOptionsModal } from './react/SelectOption'
|
||||
import widgets from './react/widgets'
|
||||
import { getItemFromBlock } from './botUtils'
|
||||
import { gamepadUiCursorState, moveGamepadCursorByPx } from './react/GamepadUiCursor'
|
||||
|
||||
// todo move this to shared file with component
|
||||
export const customKeymaps = proxy(JSON.parse(localStorage.keymap || '{}'))
|
||||
|
|
@ -45,7 +46,10 @@ export const contro = new ControMax({
|
|||
},
|
||||
ui: {
|
||||
back: [null/* 'Escape' */, 'B'],
|
||||
click: [null, 'A'],
|
||||
leftClick: [null, 'A'],
|
||||
rightClick: [null, 'Y'],
|
||||
speedupCursor: [null, 'Left Stick'],
|
||||
pauseMenu: [null, 'Start']
|
||||
},
|
||||
advanced: {
|
||||
lockUrl: ['KeyY'],
|
||||
|
|
@ -66,7 +70,7 @@ export const contro = new ControMax({
|
|||
defaultControlOptions: controlOptions,
|
||||
target: document,
|
||||
captureEvents () {
|
||||
return bot && isGameActive(false)
|
||||
return true
|
||||
},
|
||||
storeProvider: {
|
||||
load: () => customKeymaps,
|
||||
|
|
@ -86,8 +90,18 @@ const setSprinting = (state: boolean) => {
|
|||
gameAdditionalState.isSprinting = state
|
||||
}
|
||||
|
||||
contro.on('movementUpdate', ({ vector, gamepadIndex }) => {
|
||||
contro.on('movementUpdate', ({ vector, soleVector, gamepadIndex }) => {
|
||||
if (gamepadIndex !== undefined && gamepadUiCursorState.display) {
|
||||
const deadzone = 0.1 // TODO make deadzone configurable
|
||||
if (Math.abs(soleVector.x) < deadzone && Math.abs(soleVector.z) < deadzone) {
|
||||
return
|
||||
}
|
||||
moveGamepadCursorByPx(soleVector.x, true)
|
||||
moveGamepadCursorByPx(soleVector.z, false)
|
||||
emitMousemove()
|
||||
}
|
||||
miscUiState.usingGamepadInput = gamepadIndex !== undefined
|
||||
if (!bot || !isGameActive(false)) return
|
||||
// gamepadIndex will be used for splitscreen in future
|
||||
const coordToAction = [
|
||||
['z', -1, 'forward'],
|
||||
|
|
@ -145,11 +159,71 @@ subscribe(activeModalStack, () => {
|
|||
}
|
||||
})
|
||||
|
||||
const uiCommand = (command: Command) => {
|
||||
if (command === 'ui.back') {
|
||||
hideCurrentModal()
|
||||
} else if (command === 'ui.click') {
|
||||
// todo cursor
|
||||
const emitMousemove = () => {
|
||||
const { x, y } = gamepadUiCursorState
|
||||
const xAbs = x / 100 * window.innerWidth
|
||||
const yAbs = y / 100 * window.innerHeight
|
||||
const element = document.elementFromPoint(xAbs, yAbs) as HTMLElement | null
|
||||
if (!element) return
|
||||
element.dispatchEvent(new MouseEvent('mousemove', {
|
||||
clientX: xAbs,
|
||||
clientY: yAbs
|
||||
}))
|
||||
}
|
||||
|
||||
let lastClickedEl = null as HTMLElement | null
|
||||
let lastClickedElTimeout: ReturnType<typeof setTimeout> | undefined
|
||||
const inModalCommand = (command: Command, pressed: boolean) => {
|
||||
if (pressed && !gamepadUiCursorState.display) return
|
||||
|
||||
if (pressed) {
|
||||
if (command === 'ui.back') {
|
||||
hideCurrentModal()
|
||||
}
|
||||
if (command === 'ui.leftClick' || command === 'ui.rightClick') {
|
||||
// in percent
|
||||
const { x, y } = gamepadUiCursorState
|
||||
const xAbs = x / 100 * window.innerWidth
|
||||
const yAbs = y / 100 * window.innerHeight
|
||||
const el = document.elementFromPoint(xAbs, yAbs) as HTMLElement
|
||||
if (el) {
|
||||
if (el === lastClickedEl && command === 'ui.leftClick') {
|
||||
el.dispatchEvent(new MouseEvent('dblclick', {
|
||||
bubbles: true,
|
||||
clientX: xAbs,
|
||||
clientY: yAbs
|
||||
}))
|
||||
return
|
||||
}
|
||||
el.dispatchEvent(new MouseEvent('mousedown', {
|
||||
button: command === 'ui.leftClick' ? 0 : 2,
|
||||
bubbles: true,
|
||||
clientX: xAbs,
|
||||
clientY: yAbs
|
||||
}))
|
||||
el.dispatchEvent(new MouseEvent(command === 'ui.leftClick' ? 'click' : 'contextmenu', {
|
||||
bubbles: true,
|
||||
clientX: xAbs,
|
||||
clientY: yAbs
|
||||
}))
|
||||
el.dispatchEvent(new MouseEvent('mouseup', {
|
||||
button: command === 'ui.leftClick' ? 0 : 2,
|
||||
bubbles: true,
|
||||
clientX: xAbs,
|
||||
clientY: yAbs
|
||||
}))
|
||||
el.focus()
|
||||
lastClickedEl = el
|
||||
if (lastClickedElTimeout) clearTimeout(lastClickedElTimeout)
|
||||
lastClickedElTimeout = setTimeout(() => {
|
||||
lastClickedEl = null
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (command === 'ui.speedupCursor') {
|
||||
gamepadUiCursorState.multiply = pressed ? 2 : 1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -160,10 +234,7 @@ const setSneaking = (state: boolean) => {
|
|||
|
||||
const onTriggerOrReleased = (command: Command, pressed: boolean) => {
|
||||
// always allow release!
|
||||
if (pressed && !isGameActive(true)) {
|
||||
uiCommand(command)
|
||||
return
|
||||
}
|
||||
if (!bot || !isGameActive(false)) return
|
||||
if (stringStartsWith(command, 'general')) {
|
||||
// handle general commands
|
||||
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
|
||||
|
|
@ -199,7 +270,9 @@ const onTriggerOrReleased = (command: Command, pressed: boolean) => {
|
|||
}
|
||||
|
||||
// im still not sure, maybe need to refactor to handle in inventory instead
|
||||
const alwaysHandledCommand = (command: Command) => {
|
||||
const alwaysPressedHandledCommand = (command: Command) => {
|
||||
inModalCommand(command, true)
|
||||
// triggered even outside of the game
|
||||
if (command === 'general.inventory') {
|
||||
if (activeModalStack.at(-1)?.reactType?.startsWith?.('player_win:')) { // todo?
|
||||
hideCurrentModal()
|
||||
|
|
@ -207,9 +280,14 @@ const alwaysHandledCommand = (command: Command) => {
|
|||
}
|
||||
}
|
||||
|
||||
function cycleHotbarSlot (dir: 1 | -1) {
|
||||
const newHotbarSlot = (bot.quickBarSlot + dir + 9) % 9
|
||||
bot.setQuickBarSlot(newHotbarSlot)
|
||||
}
|
||||
|
||||
contro.on('trigger', ({ command }) => {
|
||||
const willContinue = !isGameActive(true)
|
||||
alwaysHandledCommand(command)
|
||||
alwaysPressedHandledCommand(command)
|
||||
if (willContinue) return
|
||||
|
||||
const secondActionCommand = secondActionCommands[command]
|
||||
|
|
@ -229,8 +307,15 @@ contro.on('trigger', ({ command }) => {
|
|||
onTriggerOrReleased(command, true)
|
||||
|
||||
if (stringStartsWith(command, 'general')) {
|
||||
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
|
||||
switch (command) {
|
||||
case 'general.jump':
|
||||
case 'general.sneak':
|
||||
case 'general.toggleSneakOrDown':
|
||||
case 'general.sprint':
|
||||
case 'general.attackDestroy':
|
||||
case 'general.interactPlace':
|
||||
// handled in onTriggerOrReleased
|
||||
break
|
||||
case 'general.inventory':
|
||||
document.exitPointerLock?.()
|
||||
openPlayerInventory()
|
||||
|
|
@ -258,6 +343,12 @@ contro.on('trigger', ({ command }) => {
|
|||
case 'general.selectItem':
|
||||
void selectItem()
|
||||
break
|
||||
case 'general.nextHotbarSlot':
|
||||
cycleHotbarSlot(1)
|
||||
break
|
||||
case 'general.prevHotbarSlot':
|
||||
cycleHotbarSlot(-1)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (command === 'advanced.lockUrl') {
|
||||
|
|
@ -278,9 +369,14 @@ contro.on('trigger', ({ command }) => {
|
|||
window.history.replaceState({}, '', `${window.location.pathname}?${newQs}`)
|
||||
// return
|
||||
}
|
||||
|
||||
if (command === 'ui.pauseMenu') {
|
||||
showModal({ reactType: 'pause-screen' })
|
||||
}
|
||||
})
|
||||
|
||||
contro.on('release', ({ command }) => {
|
||||
inModalCommand(command, false)
|
||||
onTriggerOrReleased(command, false)
|
||||
})
|
||||
|
||||
|
|
|
|||
28
src/index.ts
28
src/index.ts
|
|
@ -42,8 +42,10 @@ import { defaultsDeep } from 'lodash-es'
|
|||
|
||||
import { initVR } from './vr'
|
||||
import {
|
||||
AppConfig,
|
||||
activeModalStack,
|
||||
activeModalStacks,
|
||||
hideModal,
|
||||
insertActiveModalStack,
|
||||
isGameActive,
|
||||
miscUiState,
|
||||
|
|
@ -88,6 +90,7 @@ import { ViewerWrapper } from 'prismarine-viewer/viewer/lib/viewerWrapper'
|
|||
import './devReload'
|
||||
import './water'
|
||||
import { ConnectOptions } from './connect'
|
||||
import { subscribe } from 'valtio'
|
||||
|
||||
window.debug = debug
|
||||
window.THREE = THREE
|
||||
|
|
@ -832,7 +835,7 @@ document.body.addEventListener('touchstart', (e) => {
|
|||
void window.fetch('config.json').then(async res => res.json()).then(c => c, (error) => {
|
||||
console.warn('Failed to load optional app config.json', error)
|
||||
return {}
|
||||
}).then((config) => {
|
||||
}).then((config: AppConfig | {}) => {
|
||||
miscUiState.appConfig = config
|
||||
})
|
||||
|
||||
|
|
@ -850,8 +853,27 @@ downloadAndOpenFile().then((downloadAction) => {
|
|||
return
|
||||
}
|
||||
if (qs.get('ip') || qs.get('proxy')) {
|
||||
// show server editor for connect or save
|
||||
showModal({ reactType: 'editServer' })
|
||||
const waitAppConfigLoad = !qs.get('proxy')
|
||||
const openServerEditor = () => {
|
||||
hideModal()
|
||||
// show server editor for connect or save
|
||||
showModal({ reactType: 'editServer' })
|
||||
}
|
||||
showModal({ reactType: 'empty' })
|
||||
if (waitAppConfigLoad) {
|
||||
const unsubscribe = subscribe(miscUiState, checkCanDisplay)
|
||||
checkCanDisplay()
|
||||
// eslint-disable-next-line no-inner-declarations
|
||||
function checkCanDisplay () {
|
||||
if (miscUiState.appConfig) {
|
||||
unsubscribe()
|
||||
openServerEditor()
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
openServerEditor()
|
||||
}
|
||||
}
|
||||
|
||||
void Promise.resolve().then(() => {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import { subscribeKey } from 'valtio/utils'
|
|||
import { omitObj } from '@zardoy/utils'
|
||||
|
||||
const defaultOptions = {
|
||||
renderDistance: 2,
|
||||
multiplayerRenderDistance: 2,
|
||||
renderDistance: 3,
|
||||
multiplayerRenderDistance: 3,
|
||||
closeConfirmation: true,
|
||||
autoFullScreen: false,
|
||||
mouseRawInput: false,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import Input from './Input'
|
|||
import Button from './Button'
|
||||
import { useIsSmallWidth } from './simpleHooks'
|
||||
|
||||
export interface NewServerInfo {
|
||||
export interface BaseServerInfo {
|
||||
ip: string
|
||||
name?: string
|
||||
versionOverride?: string
|
||||
|
|
@ -15,12 +15,12 @@ export interface NewServerInfo {
|
|||
|
||||
interface Props {
|
||||
onBack: () => void
|
||||
onConfirm: (info: NewServerInfo) => void
|
||||
onConfirm: (info: BaseServerInfo) => void
|
||||
title?: string
|
||||
initialData?: NewServerInfo
|
||||
initialData?: BaseServerInfo
|
||||
parseQs?: boolean
|
||||
onQsConnect?: (server: NewServerInfo) => void
|
||||
defaults?: Pick<NewServerInfo, 'proxyOverride' | 'usernameOverride'>
|
||||
onQsConnect?: (server: BaseServerInfo) => void
|
||||
defaults?: Pick<BaseServerInfo, 'proxyOverride' | 'usernameOverride'>
|
||||
}
|
||||
|
||||
export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQs, onQsConnect, defaults }: Props) => {
|
||||
|
|
@ -33,7 +33,7 @@ export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQ
|
|||
|
||||
const [serverIp, setServerIp] = React.useState(ipWithoutPort ?? qsParams?.get('ip') ?? '')
|
||||
const [serverPort, setServerPort] = React.useState(port ?? '')
|
||||
const [versionOverride, setVersionOverride] = React.useState(initialData?.versionOverride ?? qsParams?.get('version') ?? '')
|
||||
const [versionOverride, setVersionOverride] = React.useState(initialData?.versionOverride ?? /* legacy */ initialData?.['version'] ?? qsParams?.get('version') ?? '')
|
||||
const [proxyOverride, setProxyOverride] = React.useState(initialData?.proxyOverride ?? qsParams?.get('proxy') ?? '')
|
||||
const [usernameOverride, setUsernameOverride] = React.useState(initialData?.usernameOverride ?? qsParams?.get('username') ?? '')
|
||||
const [passwordOverride, setPasswordOverride] = React.useState(initialData?.passwordOverride ?? qsParams?.get('password') ?? '')
|
||||
|
|
|
|||
|
|
@ -8,4 +8,5 @@
|
|||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
|
|
|||
11
src/react/GamepadUiCursor.module.css
Normal file
11
src/react/GamepadUiCursor.module.css
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
.crosshair {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: var(--gui-icons);
|
||||
background-size: calc(256px * var(--crosshair-scale));
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
transform: translate(-50%, -50%);
|
||||
pointer-events: none;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
40
src/react/GamepadUiCursor.tsx
Normal file
40
src/react/GamepadUiCursor.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { proxy, useSnapshot } from 'valtio'
|
||||
import { useEffect } from 'react'
|
||||
import { activeModalStack, miscUiState } from '../globalState'
|
||||
import SharedHudVars from './SharedHudVars'
|
||||
import styles from './GamepadUiCursor.module.css'
|
||||
|
||||
export const gamepadUiCursorState = proxy({
|
||||
x: 50,
|
||||
y: 50,
|
||||
multiply: 1,
|
||||
display: false
|
||||
})
|
||||
|
||||
export const moveGamepadCursorByPx = (value: number, isX: boolean) => {
|
||||
value *= gamepadUiCursorState.multiply * 3
|
||||
const valueToPercentage = value / (isX ? window.innerWidth : window.innerHeight) * 100
|
||||
gamepadUiCursorState[isX ? 'x' : 'y'] += valueToPercentage
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const hasModals = useSnapshot(activeModalStack).length > 0
|
||||
const { x, y } = useSnapshot(gamepadUiCursorState)
|
||||
const { usingGamepadInput, gameLoaded } = useSnapshot(miscUiState)
|
||||
|
||||
const doDisplay = usingGamepadInput && (hasModals || !gameLoaded)
|
||||
|
||||
useEffect(() => {
|
||||
document.body.style.cursor = gameLoaded && !hasModals && usingGamepadInput ? 'none' : 'auto'
|
||||
}, [usingGamepadInput, hasModals, gameLoaded])
|
||||
|
||||
useEffect(() => {
|
||||
gamepadUiCursorState.display = doDisplay
|
||||
}, [doDisplay])
|
||||
|
||||
if (!doDisplay) return null
|
||||
|
||||
return <SharedHudVars>
|
||||
<div className={styles.crosshair} style={{ left: `${x}%`, top: `${y}%` }} />
|
||||
</SharedHudVars>
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ interface Props extends React.ComponentProps<typeof Singleplayer> {
|
|||
username?: string
|
||||
password?: string
|
||||
proxy?: string
|
||||
version?: string
|
||||
versionOverride?: string
|
||||
shouldSave?: boolean
|
||||
}) => void
|
||||
initialProxies: SavedProxiesLocalStorage
|
||||
|
|
|
|||
|
|
@ -1,22 +1,16 @@
|
|||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { proxy } from 'valtio'
|
||||
import { proxy, useSnapshot } from 'valtio'
|
||||
import { qsOptions } from '../optionsStorage'
|
||||
import { ConnectOptions } from '../connect'
|
||||
import { hideCurrentModal, miscUiState, showModal } from '../globalState'
|
||||
import ServersList from './ServersList'
|
||||
import AddServerOrConnect from './AddServerOrConnect'
|
||||
import AddServerOrConnect, { BaseServerInfo } from './AddServerOrConnect'
|
||||
import { useDidUpdateEffect } from './utils'
|
||||
import { useIsModalActive } from './utilsApp'
|
||||
|
||||
interface StoreServerItem {
|
||||
ip: string,
|
||||
name?: string
|
||||
version?: string
|
||||
interface StoreServerItem extends BaseServerInfo {
|
||||
lastJoined?: number
|
||||
description?: string
|
||||
proxyOverride?: string
|
||||
usernameOverride?: string
|
||||
passwordOverride?: string
|
||||
optionsOverride?: Record<string, any>
|
||||
autoLogin?: Record<string, string>
|
||||
}
|
||||
|
|
@ -69,7 +63,7 @@ const getInitialServersList = () => {
|
|||
const legacyLastJoinedServer: StoreServerItem = {
|
||||
ip: localStorage['server'],
|
||||
passwordOverride: localStorage['password'],
|
||||
version: localStorage['version'],
|
||||
versionOverride: localStorage['version'],
|
||||
lastJoined: Date.now()
|
||||
}
|
||||
servers.push(legacyLastJoinedServer)
|
||||
|
|
@ -80,7 +74,7 @@ const getInitialServersList = () => {
|
|||
servers.push({
|
||||
ip: server.ip,
|
||||
description: server.description,
|
||||
version: server.version,
|
||||
versionOverride: server.version,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -206,7 +200,8 @@ const Inner = () => {
|
|||
setServersList(old => [...old, server])
|
||||
} else {
|
||||
const index = serversList.indexOf(serverEditScreen)
|
||||
serversList[index] = info
|
||||
const { lastJoined } = serversList[index]
|
||||
serversList[index] = { ...info, lastJoined }
|
||||
setServersList([...serversList])
|
||||
}
|
||||
setServerEditScreen(null)
|
||||
|
|
@ -248,7 +243,7 @@ const Inner = () => {
|
|||
username,
|
||||
server: ip,
|
||||
proxy: overrides.proxy || selectedProxy,
|
||||
botVersion: overrides.version,
|
||||
botVersion: overrides.versionOverride ?? /* legacy */ overrides['version'],
|
||||
password: overrides.password,
|
||||
ignoreQs: true,
|
||||
autoLoginPassword: server?.autoLogin?.[username],
|
||||
|
|
@ -312,7 +307,7 @@ const Inner = () => {
|
|||
return {
|
||||
name: server.index.toString(),
|
||||
title: server.name || server.ip,
|
||||
detail: (server.version ?? '') + ' ' + (server.usernameOverride ?? ''),
|
||||
detail: (server.versionOverride ?? '') + ' ' + (server.usernameOverride ?? ''),
|
||||
// lastPlayed: server.lastJoined,
|
||||
formattedTextOverride: additional?.formattedText,
|
||||
worldNameRight: additional?.textNameRight ?? '',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { CSSProperties, useEffect } from 'react'
|
|||
import icons from 'minecraft-assets/minecraft-assets/data/1.17.1/gui/icons.png'
|
||||
import widgets from 'minecraft-assets/minecraft-assets/data/1.17.1/gui/widgets.png'
|
||||
|
||||
export default ({ children }) => {
|
||||
export default ({ children }): React.ReactElement => {
|
||||
useEffect(() => {
|
||||
if (document.getElementById('hud-vars-style')) return
|
||||
// 1. Don't inline long data URLs for better DX in elements tab
|
||||
|
|
|
|||
|
|
@ -40,12 +40,14 @@ export const handleMovementStickDelta = (e?: { clientX, clientY }) => {
|
|||
}
|
||||
|
||||
joystickPointer.joystickInner!.style.transform = `translate(${x}px, ${y}px)`
|
||||
const vector = {
|
||||
x: x / max,
|
||||
y: 0,
|
||||
z: y / max,
|
||||
}
|
||||
void contro.emit('movementUpdate', {
|
||||
vector: {
|
||||
x: x / max,
|
||||
y: 0,
|
||||
z: y / max,
|
||||
},
|
||||
vector,
|
||||
soleVector: vector
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
}
|
||||
|
||||
.input {
|
||||
position: relative;
|
||||
position: absolute;
|
||||
outline: none;
|
||||
border: none;
|
||||
background: none;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import HotbarRenderApp from './react/HotbarRenderApp'
|
|||
import Crosshair from './react/Crosshair'
|
||||
import ButtonAppProvider from './react/ButtonAppProvider'
|
||||
import ServersListProvider from './react/ServersListProvider'
|
||||
import GamepadUiCursor from './react/GamepadUiCursor'
|
||||
|
||||
const RobustPortal = ({ children, to }) => {
|
||||
return createPortal(<PerComponentErrorBoundary>{children}</PerComponentErrorBoundary>, to)
|
||||
|
|
@ -149,6 +150,12 @@ const App = () => {
|
|||
{/* <GameHud>
|
||||
</GameHud> */}
|
||||
</RobustPortal>
|
||||
<RobustPortal to={document.body}>
|
||||
<div className='overlay-top-scaled'>
|
||||
<GamepadUiCursor />
|
||||
</div>
|
||||
<div></div>
|
||||
</RobustPortal>
|
||||
</ButtonAppProvider>
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,6 +134,18 @@ body {
|
|||
-ms-interpolation-mode: nearest-neighbor;
|
||||
}
|
||||
|
||||
.overlay-top-scaled {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
transform-origin: top left;
|
||||
transform: scale(var(--guiScale));
|
||||
width: calc(100% / var(--guiScale));
|
||||
height: calc(100% / var(--guiScale));
|
||||
z-index: 80;
|
||||
image-rendering: pixelated;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#viewer-canvas {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue