diff --git a/README.MD b/README.MD index 018784e3..74d4eb41 100644 --- a/README.MD +++ b/README.MD @@ -78,8 +78,6 @@ There is a builtin proxy, but you can also host your one! Just clone the repo, r [![Deploy to Koyeb](https://www.koyeb.com/static/images/deploy/button.svg)](https://app.koyeb.com/deploy?name=minecraft-web-client&type=git&repository=zardoy%2Fminecraft-web-client&branch=next&builder=dockerfile&env%5B%5D=&ports=8080%3Bhttp%3B%2F) -> **Note**: If you want to make **your own** Minecraft server accessible to web clients (without our proxies), you can use [mwc-proxy](https://github.com/zardoy/mwc-proxy) - a lightweight JS WebSocket proxy that runs on the same server as your Minecraft server, allowing players to connect directly via `wss://play.example.com`. `?client_mcraft` is added to the URL, so the proxy will know that it's this client. - Proxy servers are used to connect to Minecraft servers which use TCP protocol. When you connect connect to a server with a proxy, websocket connection is created between you (browser client) and the proxy server located in Europe, then the proxy connects to the Minecraft server and sends the data to the client (you) without any packet deserialization to avoid any additional delays. That said all the Minecraft protocol packets are processed by the client, right in your browser. ```mermaid diff --git a/config.json b/config.json index 2bfa9cfe..940fb738 100644 --- a/config.json +++ b/config.json @@ -10,10 +10,6 @@ { "ip": "wss://play.mcraft.fun" }, - { - "ip": "wss://play.webmc.fun", - "name": "WebMC" - }, { "ip": "wss://ws.fuchsmc.net" }, diff --git a/renderer/viewer/three/waypointSprite.ts b/renderer/viewer/three/waypointSprite.ts index 6a30e6db..7c8cf1f6 100644 --- a/renderer/viewer/three/waypointSprite.ts +++ b/renderer/viewer/three/waypointSprite.ts @@ -16,7 +16,7 @@ export const WAYPOINT_CONFIG = { CANVAS_SCALE: 2, ARROW: { enabledDefault: false, - pixelSize: 50, + pixelSize: 30, paddingPx: 50, }, } @@ -50,7 +50,6 @@ export function createWaypointSprite (options: { depthTest?: boolean, // Y offset in world units used by updateScaleWorld only (screen-pixel API ignores this) labelYOffset?: number, - metadata?: any, }): WaypointSprite { const color = options.color ?? 0xFF_00_00 const depthTest = options.depthTest ?? false @@ -132,22 +131,16 @@ export function createWaypointSprite (options: { canvas.height = size const ctx = canvas.getContext('2d')! ctx.clearRect(0, 0, size, size) - - // Draw arrow shape ctx.beginPath() - ctx.moveTo(size * 0.15, size * 0.5) - ctx.lineTo(size * 0.85, size * 0.5) - ctx.lineTo(size * 0.5, size * 0.15) + ctx.moveTo(size * 0.2, size * 0.5) + ctx.lineTo(size * 0.8, size * 0.5) + ctx.lineTo(size * 0.5, size * 0.2) ctx.closePath() - - // Use waypoint color for arrow - const colorHex = `#${color.toString(16).padStart(6, '0')}` - ctx.lineWidth = 6 + ctx.lineWidth = 4 ctx.strokeStyle = 'black' ctx.stroke() - ctx.fillStyle = colorHex + ctx.fillStyle = 'white' ctx.fill() - const texture = new THREE.CanvasTexture(canvas) const material = new THREE.SpriteMaterial({ map: texture, transparent: true, depthTest: false, depthWrite: false }) arrowSprite = new THREE.Sprite(material) @@ -176,9 +169,6 @@ export function createWaypointSprite (options: { ensureArrow() if (!arrowSprite) return true - // Check if onlyLeftRight is enabled in metadata - const onlyLeftRight = options.metadata?.onlyLeftRight === true - // Build camera basis using camera.up to respect custom orientations const forward = new THREE.Vector3() camera.getWorldDirection(forward) // camera look direction @@ -223,20 +213,6 @@ export function createWaypointSprite (options: { } } - // Apply onlyLeftRight logic - restrict arrows to left/right edges only - if (onlyLeftRight) { - // Force the arrow to appear only on left or right edges - if (Math.abs(rx) > Math.abs(ry)) { - // Horizontal direction is dominant, keep it - ry = 0 - } else { - // Vertical direction is dominant, but we want only left/right - // So choose left or right based on the sign of rx - rx = rx >= 0 ? 1 : -1 - ry = 0 - } - } - // Place on the rectangle border [-1,1]x[-1,1] const s = Math.max(Math.abs(rx), Math.abs(ry)) || 1 let ndcX = rx / s diff --git a/renderer/viewer/three/waypoints.ts b/renderer/viewer/three/waypoints.ts index 256ca6df..cebd779a 100644 --- a/renderer/viewer/three/waypoints.ts +++ b/renderer/viewer/three/waypoints.ts @@ -17,7 +17,6 @@ interface WaypointOptions { color?: number label?: string minDistance?: number - metadata?: any } export class WaypointsRenderer { @@ -72,14 +71,13 @@ export class WaypointsRenderer { this.removeWaypoint(id) const color = options.color ?? 0xFF_00_00 - const { label, metadata } = options + const { label } = options const minDistance = options.minDistance ?? 0 const sprite = createWaypointSprite({ position: new THREE.Vector3(x, y, z), color, label: (label || id), - metadata, }) sprite.enableOffscreenArrow(true) sprite.setArrowParent(this.waypointScene) diff --git a/src/appConfig.ts b/src/appConfig.ts index c29d74e8..92fde21a 100644 --- a/src/appConfig.ts +++ b/src/appConfig.ts @@ -35,7 +35,7 @@ export type AppConfig = { // defaultVersion?: string peerJsServer?: string peerJsServerFallback?: string - promoteServers?: Array<{ ip, description, name?, version?, }> + promoteServers?: Array<{ ip, description, version? }> mapsProvider?: string appParams?: Record // query string params diff --git a/src/basicSounds.ts b/src/basicSounds.ts index 54af0d35..37f8dccd 100644 --- a/src/basicSounds.ts +++ b/src/basicSounds.ts @@ -7,12 +7,7 @@ let audioContext: AudioContext const sounds: Record = {} // Track currently playing sounds and their gain nodes -const activeSounds: Array<{ - source: AudioBufferSourceNode; - gainNode: GainNode; - volumeMultiplier: number; - isMusic: boolean; -}> = [] +const activeSounds: Array<{ source: AudioBufferSourceNode; gainNode: GainNode; volumeMultiplier: number }> = [] window.activeSounds = activeSounds // load as many resources on page load as possible instead on demand as user can disable internet connection after he thinks the page is loaded @@ -48,7 +43,7 @@ export async function loadSound (path: string, contents = path) { } } -export const loadOrPlaySound = async (url, soundVolume = 1, loadTimeout = options.remoteSoundsLoadTimeout, loop = false, isMusic = false) => { +export const loadOrPlaySound = async (url, soundVolume = 1, loadTimeout = options.remoteSoundsLoadTimeout, loop = false) => { const soundBuffer = sounds[url] if (!soundBuffer) { const start = Date.now() @@ -56,11 +51,11 @@ export const loadOrPlaySound = async (url, soundVolume = 1, loadTimeout = option if (cancelled || Date.now() - start > loadTimeout) return } - return playSound(url, soundVolume, loop, isMusic) + return playSound(url, soundVolume, loop) } -export async function playSound (url, soundVolume = 1, loop = false, isMusic = false) { - const volume = soundVolume * (options.volume / 100) * (isMusic ? options.musicVolume / 100 : 1) +export async function playSound (url, soundVolume = 1, loop = false) { + const volume = soundVolume * (options.volume / 100) if (!volume) return @@ -87,7 +82,7 @@ export async function playSound (url, soundVolume = 1, loop = false, isMusic = f source.start(0) // Add to active sounds - activeSounds.push({ source, gainNode, volumeMultiplier: soundVolume, isMusic }) + activeSounds.push({ source, gainNode, volumeMultiplier: soundVolume }) const callbacks = [] as Array<() => void> source.onended = () => { @@ -115,7 +110,6 @@ export async function playSound (url, soundVolume = 1, loop = false, isMusic = f console.warn('Failed to stop sound:', err) } }, - gainNode, } } @@ -143,11 +137,11 @@ export function stopSound (url: string) { } } -export function changeVolumeOfCurrentlyPlayingSounds (newVolume: number, newMusicVolume: number) { +export function changeVolumeOfCurrentlyPlayingSounds (newVolume: number) { const normalizedVolume = newVolume / 100 - for (const { gainNode, volumeMultiplier, isMusic } of activeSounds) { + for (const { gainNode, volumeMultiplier } of activeSounds) { try { - gainNode.gain.value = normalizedVolume * volumeMultiplier * (isMusic ? newMusicVolume / 100 : 1) + gainNode.gain.value = normalizedVolume * volumeMultiplier } catch (err) { console.warn('Failed to change sound volume:', err) } @@ -155,9 +149,5 @@ export function changeVolumeOfCurrentlyPlayingSounds (newVolume: number, newMusi } subscribeKey(options, 'volume', () => { - changeVolumeOfCurrentlyPlayingSounds(options.volume, options.musicVolume) -}) - -subscribeKey(options, 'musicVolume', () => { - changeVolumeOfCurrentlyPlayingSounds(options.volume, options.musicVolume) + changeVolumeOfCurrentlyPlayingSounds(options.volume) }) diff --git a/src/customChannels.ts b/src/customChannels.ts index 506ea776..b566f9dd 100644 --- a/src/customChannels.ts +++ b/src/customChannels.ts @@ -82,30 +82,15 @@ const registerWaypointChannels = () => { { name: 'color', type: 'i32' - }, - { - name: 'metadataJson', - type: ['pstring', { countType: 'i16' }] } ] ] registerChannel('minecraft-web-client:waypoint-add', packetStructure, (data) => { - // Parse metadata if provided - let metadata: any = {} - if (data.metadataJson && data.metadataJson.trim() !== '') { - try { - metadata = JSON.parse(data.metadataJson) - } catch (error) { - console.warn('Failed to parse waypoint metadataJson:', error) - } - } - getThreeJsRendererMethods()?.addWaypoint(data.id, data.x, data.y, data.z, { minDistance: data.minDistance, label: data.label || undefined, - color: data.color || undefined, - metadata + color: data.color || undefined }) }) diff --git a/src/defaultOptions.ts b/src/defaultOptions.ts index 48c1cfad..85ebae17 100644 --- a/src/defaultOptions.ts +++ b/src/defaultOptions.ts @@ -16,8 +16,7 @@ export const defaultOptions = { chatOpacityOpened: 100, messagesLimit: 200, volume: 50, - enableMusic: true, - musicVolume: 50, + enableMusic: false, // fov: 70, fov: 75, defaultPerspective: 'first_person' as 'first_person' | 'third_person_back' | 'third_person_front', diff --git a/src/devtools.ts b/src/devtools.ts index 1f8ef8e8..6c47f73d 100644 --- a/src/devtools.ts +++ b/src/devtools.ts @@ -5,17 +5,6 @@ import { WorldRendererThree } from 'renderer/viewer/three/worldrendererThree' import { enable, disable, enabled } from 'debug' import { Vec3 } from 'vec3' -customEvents.on('mineflayerBotCreated', () => { - window.debugServerPacketNames = Object.fromEntries(Object.keys(loadedData.protocol.play.toClient.types).map(name => { - name = name.replace('packet_', '') - return [name, name] - })) - window.debugClientPacketNames = Object.fromEntries(Object.keys(loadedData.protocol.play.toServer.types).map(name => { - name = name.replace('packet_', '') - return [name, name] - })) -}) - window.Vec3 = Vec3 window.cursorBlockRel = (x = 0, y = 0, z = 0) => { const newPos = bot.blockAtCursor(5)?.position.offset(x, y, z) diff --git a/src/entities.ts b/src/entities.ts index 674f91ef..dcec6143 100644 --- a/src/entities.ts +++ b/src/entities.ts @@ -246,29 +246,22 @@ customEvents.on('gameLoaded', () => { } } // even if not found, still record to cache - void getThreeJsRendererMethods()!.updatePlayerSkin(entityId, player.username, player.uuid, skinUrl ?? true, capeUrl) + void getThreeJsRendererMethods()?.updatePlayerSkin(entityId, player.username, player.uuid, skinUrl ?? true, capeUrl) } catch (err) { - reportError(new Error('Error applying skin texture:', { cause: err })) + console.error('Error decoding player texture:', err) } } bot.on('playerJoined', updateSkin) bot.on('playerUpdated', updateSkin) - for (const entity of Object.values(bot.players)) { - updateSkin(entity) - } - const teamUpdated = (team: Team) => { + 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) } } - } - bot.on('teamUpdated', teamUpdated) - for (const team of Object.values(bot.teams)) { - teamUpdated(team) - } + }) const updateEntityNameTags = (team: Team) => { for (const entity of Object.values(bot.entities)) { diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index d40260df..d16fee20 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -470,7 +470,6 @@ const openWindow = (type: string | undefined, title: string | any = undefined) = const isRightClick = type === 'rightclick' const isLeftClick = type === 'leftclick' if (isLeftClick || isRightClick) { - modelViewerState.model = undefined inv.canvasManager.children[0].showRecipesOrUsages(isLeftClick, item) } } else { @@ -502,7 +501,6 @@ const openWindow = (type: string | undefined, title: string | any = undefined) = if (freeSlot === null) return void bot.creative.setInventorySlot(freeSlot, item) } else { - modelViewerState.model = undefined inv.canvasManager.children[0].showRecipesOrUsages(!isRightclick, mapSlots([item], true)[0]) } } diff --git a/src/mineflayer/websocket-core.ts b/src/mineflayer/websocket-core.ts index f8163102..0edd2497 100644 --- a/src/mineflayer/websocket-core.ts +++ b/src/mineflayer/websocket-core.ts @@ -15,12 +15,9 @@ class CustomDuplex extends Duplex { } export const getWebsocketStream = async (host: string) => { - const baseProtocol = host.startsWith('ws://') ? 'ws' : 'wss' + const baseProtocol = location.protocol === 'https:' ? 'wss' : host.startsWith('ws://') ? 'ws' : 'wss' const hostClean = host.replace('ws://', '').replace('wss://', '') - const hostURL = new URL(`${baseProtocol}://${hostClean}`) - const hostParams = hostURL.searchParams - hostParams.append('client_mcraft', '') - const ws = new WebSocket(`${baseProtocol}://${hostURL.host}${hostURL.pathname}?${hostParams.toString()}`) + const ws = new WebSocket(`${baseProtocol}://${hostClean}`) const clientDuplex = new CustomDuplex(undefined, data => { ws.send(data) }) diff --git a/src/optionsGuiScheme.tsx b/src/optionsGuiScheme.tsx index 0cb0fe1e..a47c06eb 100644 --- a/src/optionsGuiScheme.tsx +++ b/src/optionsGuiScheme.tsx @@ -480,24 +480,6 @@ export const guiOptionsScheme: { ], sound: [ { volume: {} }, - { - custom () { - return { - options.musicVolume = value - }} - item={{ - type: 'slider', - id: 'musicVolume', - text: 'Music Volume', - min: 0, - max: 100, - unit: '%', - }} - /> - }, - }, { custom () { return