Skip to content

Commit 317a335

Browse files
authored
Release (#461)
2 parents e270c0e + 39ea21a commit 317a335

24 files changed

Lines changed: 844 additions & 130 deletions

.github/workflows/next-deploy.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Vercel Deploy Next
1+
name: Vercel Deploy Next (Beta)
22
env:
33
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
44
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
@@ -63,7 +63,7 @@ jobs:
6363
path: cypress/screenshots/
6464
- name: Set deployment aliases
6565
run: |
66-
for alias in $(echo ${{ secrets.TEST_PREVIEW_DOMAIN }} | tr "," "\n"); do
66+
for alias in $(echo ${{ vars.TEST_PREVIEW_DOMAIN }} | tr "," "\n"); do
6767
vercel alias set ${{ steps.deploy.outputs.stdout }} $alias --token=${{ secrets.VERCEL_TOKEN }} --scope=zaro
6868
done
6969

.github/workflows/preview.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,6 @@ jobs:
7979
env:
8080
CONFIG_JSON_SOURCE: BUNDLED
8181
LOCAL_CONFIG_FILE: config.mcraft-only.json
82-
- name: Copy playground files
83-
run: |
84-
mkdir -p .vercel/output/static/playground
85-
pnpm build-playground
86-
cp -r renderer/dist/* .vercel/output/static/playground/
8782
- name: Write pr redirect index.html
8883
run: |
8984
mkdir -p .vercel/output/static/pr

README.MD

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,10 +225,10 @@ Only during development:
225225

226226
### Notable Things that Power this Project
227227

228-
- [Mineflayer](https://github.com/PrismarineJS/mineflayer) - Handles all client-side communications with the server (including the built-in one)
228+
- [Mineflayer](https://github.com/GenerelSchwerz/mineflayer) - Handles all client-side communications with the server (including the built-in one)
229229
- [Forked Flying Squid (Space Squid)](https://github.com/zardoy/space-squid) - The built-in offline server that makes singleplayer & P2P possible!
230230
- [Prismarine Provider Anvil](https://github.com/PrismarineJS/prismarine-provider-anvil) - Handles world loading (region format)
231-
- [Prismarine Physics](https://github.com/PrismarineJS/prismarine-physics) - Does all the physics calculations
231+
- [Prismarine Physics](https://github.com/nxg-org/mineflayer-physics-utils/tree/master/src/physics/engines/botcraft.ts) - Does all the physics calculations
232232
- [Minecraft Protocol](https://github.com/PrismarineJS/node-minecraft-protocol) - Makes connections to servers possible
233233
- [Peer.js](https://peerjs.com/) - P2P networking (when you open to wan)
234234
- [Three.js](https://threejs.org/) - Helping in 3D rendering

src/core/iframeChannels.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { proxy } from 'valtio'
2+
3+
export const iframeState = proxy({
4+
id: '',
5+
url: '',
6+
title: '',
7+
metadata: null as Record<string, any> | null,
8+
})
9+
globalThis.iframeState = iframeState
10+
11+
export const registerIframeChannels = () => {
12+
registerIframeOpenChannel()
13+
}
14+
15+
const registerIframeOpenChannel = () => {
16+
const CHANNEL_NAME = 'minecraft-web-client:iframe-open'
17+
18+
const packetStructure = [
19+
'container',
20+
[
21+
{
22+
name: 'id',
23+
type: ['pstring', { countType: 'i16' }]
24+
},
25+
{
26+
name: 'url',
27+
type: ['pstring', { countType: 'i16' }]
28+
},
29+
{
30+
name: 'title',
31+
type: ['pstring', { countType: 'i16' }]
32+
},
33+
{
34+
name: 'metadataJson',
35+
type: ['pstring', { countType: 'i16' }]
36+
}
37+
]
38+
]
39+
40+
bot._client.registerChannel(CHANNEL_NAME, packetStructure, true)
41+
42+
bot._client.on(CHANNEL_NAME as any, (data) => {
43+
const { id, url, title, metadataJson } = data
44+
45+
let metadata: Record<string, any> | null = null
46+
if (metadataJson && metadataJson.trim() !== '') {
47+
try {
48+
metadata = JSON.parse(metadataJson)
49+
} catch (error) {
50+
console.warn('Failed to parse iframe metadataJson:', error)
51+
}
52+
}
53+
54+
iframeState.id = id
55+
iframeState.url = url
56+
iframeState.title = title || ''
57+
iframeState.metadata = metadata
58+
})
59+
60+
console.debug(`registered custom channel ${CHANNEL_NAME} channel`)
61+
}

src/customChannels.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import { getThreeJsRendererMethods } from 'renderer/viewer/three/threeJsMethods'
44
import { options, serverChangedSettings } from './optionsStorage'
55
import { jeiCustomCategories } from './inventoryWindows'
66
import { registerIdeChannels } from './core/ideChannels'
7+
import { registerIframeChannels } from './core/iframeChannels'
78
import { serverSafeSettings } from './defaultOptions'
89
import { lastConnectOptions } from './appStatus'
10+
import { gameAdditionalState } from './globalState'
911

1012
const isWebSocketServer = (server: string | undefined) => {
1113
if (!server) return false
@@ -30,7 +32,9 @@ export default () => {
3032
registerWaypointChannels()
3133
registerFireworksChannels()
3234
registerIdeChannels()
35+
registerIframeChannels()
3336
registerServerSettingsChannel()
37+
registerTypingIndicatorChannel()
3438
})
3539
})
3640
}
@@ -611,6 +615,41 @@ const registerServerSettingsChannel = () => {
611615
}, false) // Don't wait for world, settings can be applied before world loads
612616
}
613617

618+
const registerTypingIndicatorChannel = () => {
619+
const CHANNEL_NAME = 'minecraft-web-client:typing-indicator'
620+
const packetStructure = [
621+
'container',
622+
[
623+
{
624+
name: 'username',
625+
type: ['pstring', { countType: 'i16' }]
626+
},
627+
{
628+
name: 'isTyping',
629+
type: 'bool'
630+
}
631+
]
632+
]
633+
634+
registerChannel(CHANNEL_NAME, packetStructure, (data) => {
635+
const { username, isTyping } = data
636+
637+
if (isTyping) {
638+
// Add user to typing list if not already there
639+
const existingIndex = gameAdditionalState.typingUsers.findIndex(user => user.username === username)
640+
if (existingIndex === -1) {
641+
gameAdditionalState.typingUsers.push({ username, timestamp: Date.now() })
642+
} else {
643+
// Update timestamp for existing user
644+
gameAdditionalState.typingUsers[existingIndex].timestamp = Date.now()
645+
}
646+
} else {
647+
// Remove user from typing list
648+
gameAdditionalState.typingUsers = gameAdditionalState.typingUsers.filter(user => user.username !== username)
649+
}
650+
})
651+
}
652+
614653
function getCurrentTopDomain (): string {
615654
const { hostname } = location
616655
// Split hostname into parts

src/customCommands.ts

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { guiOptionsScheme, tryFindOptionConfig } from './optionsGuiScheme'
1+
import { optionsMeta } from './defaultOptions'
22
import { options } from './optionsStorage'
33

44
export const customCommandsConfig = {
@@ -29,22 +29,29 @@ export const customCommandsConfig = {
2929
if (!action || value === undefined || action === 'toggle') return null
3030
if (action === 'set') {
3131
const getBase = () => {
32-
const config = tryFindOptionConfig(setting as any)
33-
if (config && 'values' in config) {
32+
const config = optionsMeta[setting as keyof typeof optionsMeta]
33+
if (config?.possibleValues && config.possibleValues.length > 0) {
34+
// Handle both string[] and Array<[string, string]> formats
35+
const { possibleValues } = config
36+
const options = Array.isArray(possibleValues[0]) && typeof possibleValues[0][0] === 'string'
37+
? (possibleValues as Array<[string, string]>).map(([val]) => val)
38+
: possibleValues as string[]
3439
return {
3540
type: 'select',
36-
options: config.values
41+
options
3742
}
3843
}
39-
if (config?.type === 'toggle' || typeof value === 'boolean') {
44+
if (typeof value === 'boolean') {
4045
return {
4146
type: 'select',
4247
options: ['true', 'false']
4348
}
4449
}
45-
if (config?.type === 'slider' || value.type === 'number') {
50+
if (typeof value === 'number') {
4651
return {
4752
type: 'number',
53+
min: config?.min,
54+
max: config?.max,
4855
}
4956
}
5057
return {
@@ -53,25 +60,37 @@ export const customCommandsConfig = {
5360
}
5461
return {
5562
...getBase(),
56-
placeholder: value
63+
placeholder: String(value)
5764
}
5865
}
5966
}
6067
],
6168
handler ([setting, action, value]) {
6269
if (action === 'toggle' || action === undefined) {
63-
const value = options[setting]
64-
const config = tryFindOptionConfig(setting)
65-
if (config && 'values' in config && config.values) {
66-
const { values } = config
67-
const currentIndex = values.indexOf(value)
70+
const currentValue = options[setting]
71+
const config = optionsMeta[setting as keyof typeof optionsMeta]
72+
if (config?.possibleValues && config.possibleValues.length > 0) {
73+
// Handle both string[] and Array<[string, string]> formats
74+
const { possibleValues } = config
75+
const values = Array.isArray(possibleValues[0]) && typeof possibleValues[0][0] === 'string'
76+
? (possibleValues as Array<[string, string]>).map(([val]) => val)
77+
: possibleValues as string[]
78+
const currentIndex = values.indexOf(String(currentValue))
6879
const nextIndex = (currentIndex + 1) % values.length
69-
options[setting] = values[nextIndex]
80+
options[setting] = values[nextIndex] as any
7081
} else {
71-
options[setting] = typeof value === 'boolean' ? !value : typeof value === 'number' ? value + 1 : value
82+
options[setting] = typeof currentValue === 'boolean' ? !currentValue : typeof currentValue === 'number' ? currentValue + 1 : currentValue
7283
}
7384
} else {
74-
options[setting] = value
85+
// Convert string values to appropriate types
86+
const config = optionsMeta[setting as keyof typeof optionsMeta]
87+
let convertedValue: any = value
88+
if (typeof options[setting] === 'boolean') {
89+
convertedValue = value === 'true' || value === true
90+
} else if (typeof options[setting] === 'number') {
91+
convertedValue = Number(value)
92+
}
93+
options[setting] = convertedValue
7594
}
7695
}
7796
},

src/defaultOptions.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,89 @@ export const serverSafeSettings: Partial<Record<keyof typeof defaultOptions, tru
202202
newVersionsLighting: true,
203203
showCursorBlockInSpectator: true,
204204
}
205+
export type OptionValueType = string | number | boolean | string[] | Record<string, any> | null
206+
207+
export type OptionPossibleValues =
208+
| string[]
209+
| Array<[string, string]> // [value, label] tuples
210+
211+
export type OptionMeta = {
212+
possibleValues?: OptionPossibleValues
213+
isCustomInput?: boolean // If true, use showInputsModal for string input
214+
min?: number
215+
max?: number
216+
unit?: string
217+
text?: string
218+
tooltip?: string
219+
}
220+
221+
export const optionsMeta: Partial<Record<keyof typeof defaultOptions, OptionMeta>> = {
222+
gpuPreference: {
223+
possibleValues: [['default', 'Auto'], ['high-performance', 'Dedicated'], ['low-power', 'Low Power']]
224+
},
225+
backgroundRendering: {
226+
possibleValues: [
227+
['full', 'NO'],
228+
['5fps', '5 FPS'],
229+
['20fps', '20 FPS'],
230+
]
231+
},
232+
activeRenderer: {
233+
possibleValues: [
234+
['threejs', 'Three.js (stable)'],
235+
]
236+
},
237+
renderDebug: {
238+
possibleValues: ['advanced', 'basic', 'none']
239+
},
240+
serverResourcePacks: {
241+
possibleValues: ['prompt', 'always', 'never']
242+
},
243+
showMinimap: {
244+
possibleValues: ['always', 'singleplayer', 'never']
245+
},
246+
highlightBlockColor: {
247+
possibleValues: [
248+
['auto', 'Auto'],
249+
['blue', 'Blue'],
250+
['classic', 'Classic']
251+
]
252+
},
253+
wysiwygSignEditor: {
254+
possibleValues: ['auto', 'always', 'never']
255+
},
256+
touchMovementType: {
257+
possibleValues: [['modern', 'Modern'], ['classic', 'Classic']]
258+
},
259+
touchInteractionType: {
260+
possibleValues: [['classic', 'Classic'], ['buttons', 'Buttons']]
261+
},
262+
autoJump: {
263+
possibleValues: ['always', 'auto', 'never']
264+
},
265+
saveLoginPassword: {
266+
possibleValues: ['prompt', 'always', 'never']
267+
},
268+
packetsLoggerPreset: {
269+
possibleValues: [
270+
['all', 'All'],
271+
['no-buffers', 'No Buffers']
272+
]
273+
},
274+
// Custom string inputs (will use showInputsModal)
275+
localUsername: {
276+
isCustomInput: true
277+
},
278+
guestUsername: {
279+
isCustomInput: true
280+
},
281+
language: {
282+
isCustomInput: true
283+
},
284+
enabledResourcepack: {
285+
isCustomInput: true
286+
},
287+
useVersionsTextures: {
288+
isCustomInput: true
289+
}
290+
}

src/globalState.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ export const gameAdditionalState = proxy({
172172
viewerConnection: false,
173173

174174
usingServerResourcePack: false,
175+
typingUsers: [] as Array<{ username: string; timestamp: number }>,
175176
})
176177

177178
window.gameAdditionalState = gameAdditionalState

0 commit comments

Comments
 (0)