Electron fixes after RC feedbacks (#1784)

This commit is contained in:
lucas lelievre
2026-03-26 05:15:41 +01:00
committed by GitHub
parent 5e7816d72d
commit ed96742680
6 changed files with 123 additions and 37 deletions

View File

@@ -231,6 +231,24 @@ jobs:
name: release-android
path: SlimeVR-android.apk
- name: Build Google Play release bundle
if: startsWith(github.ref, 'refs/tags/')
run: ./gradlew :server:android:bundleRelease
env:
ANDROID_STORE_FILE: ${{ secrets.ANDROID_GPLAY_STORE_FILE }}
ANDROID_STORE_PASSWD: ${{ secrets.ANDROID_GPLAY_STORE_PASSWD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_GPLAY_KEY_ALIAS }}
ANDROID_KEY_PASSWD: ${{ secrets.ANDROID_GPLAY_KEY_PASSWD }}
- name: Upload the Google Play artifact
uses: actions/upload-artifact@v6
if: startsWith(github.ref, 'refs/tags/')
with:
# Artifact name
name: 'SlimeVR-Android-GPDev' # optional, default is artifact
# A file, directory or wildcard pattern that describes what to upload
path: server/android/build/outputs/bundle/release/*
create-release:
name: Finalize Release Draft
needs: [package-desktop, bundle-android, build-server-jar, build-gui-frontend]

View File

@@ -14,7 +14,7 @@ import { IPC_CHANNELS } from '../shared';
import path, { dirname, join } from 'path';
import open from 'open';
import trayIcon from '../resources/icons/icon.png?asset';
import appleTrayIcon from '../resources/icons/appleTrayIcon.png?asset';
import appleTrayIcon from '../resources/icons/Square30x30Logo.png?asset';
import { readFile, stat } from 'fs/promises';
import { getPlatform, handleIpc, isPortAvailable } from './utils';
import {
@@ -26,7 +26,7 @@ import {
getWindowStateFile,
} from './paths';
import { stores } from './store';
import { logger } from './logger';
import { closeLogger, logger } from './logger';
import { writeFileSync } from 'node:fs';
import { spawn } from 'node:child_process';
@@ -36,11 +36,16 @@ import { ServerStatusEvent } from 'electron/preload/interface';
import { mkdir } from 'node:fs/promises';
import { MenuItem } from 'electron/main';
// Fixes colors looking washed on linux
// Might affect hdr
if (process.platform === 'linux') {
app.commandLine.appendSwitch('disable-features', 'WaylandWpColorManagerV1');
app.commandLine.appendSwitch('force-color-profile', 'srgb');
}
app.setPath('userData', getGuiDataFolder())
app.setPath('sessionData', join(getGuiDataFolder(), 'electron'))
app.setPath('userData', getGuiDataFolder());
app.setPath('sessionData', join(getGuiDataFolder(), 'electron'));
// Register custom protocol to handle asset paths with leading slashes
protocol.registerSchemesAsPrivileged([
{
scheme: 'app',
@@ -339,8 +344,7 @@ function createWindow() {
menu.append(new MenuItem({ label: 'Copy', role: 'copy' }));
menu.append(new MenuItem({ label: 'Paste', role: 'paste' }));
if (mainWindow)
menu.popup({ window: mainWindow });
if (mainWindow) menu.popup({ window: mainWindow });
});
}
@@ -353,7 +357,7 @@ const checkEnvironmentVariables = () => {
'SlimeVR',
`You have environment variables ${set.join(', ')} set, which may cause the SlimeVR Server to fail to launch properly.`
);
app.exit(0);
app.quit();
}
};
@@ -380,36 +384,60 @@ const spawnServer = async () => {
'SlimeVR',
`Couldn't find a compatible Java version, please download Java 17 or higher`
);
app.exit(0);
app.quit()
return;
}
logger.info({ javaBin, serverJar }, 'Found Java and server jar');
const process = spawn(javaBin, ['-Xmx128M', '-jar', serverJar, 'run']);
process.stdout?.on('data', (message) => {
mainWindow?.webContents.send(IPC_CHANNELS.SERVER_STATUS, {
message: message.toString(),
type: 'stdout',
} satisfies ServerStatusEvent);
const platform = getPlatform();
const serverWorkdir = getServerDataFolder()
const serverProcess = spawn(javaBin, ['-Xmx128M', '-jar', serverJar, 'run'], {
cwd: serverWorkdir,
shell: false,
env:
platform === 'windows'
? {
...process.env,
APPDATA: app.getPath('appData'),
LOCALAPPDATA: process.env['USERPROFILE'] ? path.join(process.env['USERPROFILE'], 'AppData', 'Local') : undefined,
}
: undefined,
});
process.stderr?.on('data', (message) => {
mainWindow?.webContents.send(IPC_CHANNELS.SERVER_STATUS, {
message: message.toString(),
type: 'stderr',
} satisfies ServerStatusEvent);
const sendToWindow = (event: ServerStatusEvent) => {
if (mainWindow && !mainWindow.webContents.isDestroyed()) {
mainWindow.webContents.send(IPC_CHANNELS.SERVER_STATUS, event);
}
};
serverProcess.stdout?.on('data', (message) => {
sendToWindow({ message: message.toString(), type: 'stdout' });
});
serverProcess.stderr?.on('data', (message) => {
sendToWindow({ message: message.toString(), type: 'stderr' });
});
serverProcess.on('error', (err) => {
logger.info({ err }, 'Error launching the java server');
if (!isQuitting) app.quit();
})
serverProcess.on('exit', () => {
logger.info('Server process exiting');
})
const exited = new Promise<void>((resolve) => serverProcess.once('exit', resolve));
return {
process: process,
close: () => {
process.kill('SIGTERM');
},
process: serverProcess,
close: () => serverProcess.kill(),
waitForExit: () => exited,
};
};
let isQuitting = false;
app.whenReady().then(async () => {
// Register protocol handler for app:// scheme to handle assets with leading slashes
protocol.handle('app', (request) => {
@@ -431,18 +459,18 @@ app.whenReady().then(async () => {
}
});
process.on('exit', () => {
server?.close();
});
app.on('before-quit', async () => {
app.on('before-quit', async (event) => {
if (isQuitting) return;
isQuitting = true;
event.preventDefault();
logger.info('App quitting, saving...');
server?.close();
await server?.waitForExit();
stores.settings.save();
stores.cache.save();
discordPresence.destroy();
await saveWindowState();
await closeLogger();
app.exit(0);
});
});

View File

@@ -24,3 +24,11 @@ const transport = pino.transport({
});
export const logger = pino(transport);
export const closeLogger = () =>
new Promise<void>((resolve) => {
logger.flush(() => {
transport.once('close', resolve);
transport.end();
});
});

View File

@@ -103,12 +103,12 @@ export const findSystemJRE = async (sharedDir: string) => {
export const findServerJar = () => {
const paths = [
options.path ? path.resolve(options.path) : undefined,
app.isPackaged ? path.resolve(process.resourcesPath) : undefined,
// AppImage passes the fakeroot in `APPDIR` env var.
process.env['APPDIR']
? path.resolve(join(process.env['APPDIR'], 'usr/share/slimevr/'))
: undefined,
path.dirname(app.getPath('exe')),
// For flatpack container
path.resolve('/app/share/slimevr/'),
path.resolve('/usr/share/slimevr/'),

View File

@@ -19,6 +19,10 @@ class BVHRecorder(server: VRServer) {
val file = if (filePath.isDirectory()) {
getBvhFile(filePath) ?: return
} else {
if (filePath.extension != "bvh") {
LogManager.severe("[BVH] Invalid file extension for bvh file \"${filePath}\".")
return
}
filePath
}

View File

@@ -44,6 +44,18 @@ class UDPDevice(
@JvmField
var lastPacketNumber: Long = -1
@JvmField
var totalPacketsReceived: Int = 0
@JvmField
var acceptedPackets: Int = 0
@JvmField
var lastPacketCounterReset: Long = System.currentTimeMillis()
val packetLossPercent: Float
get() = if (totalPacketsReceived == 0) 0f else (1f - acceptedPackets.toFloat() / totalPacketsReceived.toFloat())
@JvmField
var protocol: NetworkProtocol? = null
@@ -68,9 +80,25 @@ class UDPDevice(
var firmwareFeatures = FirmwareFeatures()
fun isNextPacket(packetId: Long): Boolean {
if (packetId != 0L && packetId <= lastPacketNumber) return false
lastPacketNumber = packetId
return true
val now = System.currentTimeMillis()
if (now - lastPacketCounterReset >= 10_000L) {
totalPacketsReceived = 0
acceptedPackets = 0
lastPacketCounterReset = now
}
totalPacketsReceived++
val accepted = packetId == 0L || packetId > lastPacketNumber
if (accepted) {
lastPacketNumber = packetId
acceptedPackets++
}
val lost = totalPacketsReceived - acceptedPackets
trackers.values.forEach {
it.packetsReceived = totalPacketsReceived
it.packetsLost = lost
it.packetLoss = packetLossPercent
}
return accepted
}
override fun toString(): String = "udp:/$ipAddress"