Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/common/AmongUsState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,6 @@ export interface VoiceState {
localIsAlive: boolean;
muted: boolean;
deafened: boolean;
effectivelyMuted: boolean;
mod: ModsType;
}
119 changes: 117 additions & 2 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ import installExtension, { REACT_DEVELOPER_TOOLS } from 'electron-devtools-insta
import { gameReader } from './hook';
import { GenerateHat } from './avatarGenerator';
const args = require('minimist')(process.argv); // eslint-disable-line
import * as http from 'http';
import { readFileSync } from 'fs';
const isDevelopment = process.env.NODE_ENV !== 'production';
const devTools = (isDevelopment || args.dev === 1) && true;
const appVersion: string = isDevelopment? "DEV" : autoUpdater.currentVersion.version;
const appVersion: string = isDevelopment ? "DEV" : autoUpdater.currentVersion.version;

declare global {
namespace NodeJS {
Expand All @@ -35,6 +37,117 @@ declare global {
// global reference to mainWindow (necessary to prevent window from being garbage collected)
global.mainWindow = null;
global.overlay = null;

let isMutedGlobal = false;
let isDeafenedGlobal = false;

export function updateMuteStatus(muted: boolean, deafened: boolean) {
isMutedGlobal = muted;
isDeafenedGlobal = deafened;
}

function startMicrophoneStatusServer() {
const server = http.createServer((req, res) => {
if (!req.url) return;

if (req.url.startsWith('/status')) {
res.writeHead(200, {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache',
'Expires': '0'
});
res.end(JSON.stringify({ muted: isMutedGlobal, deafened: isDeafenedGlobal }));
return;
}

if (req.url === '/Muted.png' || req.url === '/Unmuted.png' || req.url.startsWith('/icon')) {
const iconName = req.url.startsWith('/icon') ? (isMutedGlobal || isDeafenedGlobal ? 'Muted.png' : 'Unmuted.png') : req.url.slice(1).split('?')[0];

const possiblePaths = isDevelopment ? [
joinPath(__dirname, '..', '..', 'static', iconName),
joinPath(process.cwd(), 'static', iconName),
joinPath(__dirname, 'static', iconName)
] : [
joinPath(__dirname, '..', 'renderer', 'static', iconName),
joinPath(app.getAppPath(), 'dist', 'renderer', 'static', iconName),
joinPath(process.resourcesPath, 'static', iconName),
joinPath(__dirname, iconName)
];

let image = null;
for (const path of possiblePaths) {
try {
image = readFileSync(path);
if (image) break;
} catch (e) { }
}

if (image) {
res.writeHead(200, {
'Content-Type': 'image/png',
'Access-Control-Allow-Origin': '*',
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache',
'Expires': '0'
});
res.end(image);
} else {
res.writeHead(404);
res.end();
}
return;
}

res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
<!DOCTYPE html>
<html>
<head>
<title>Microphone Status Overlay</title>
<style>
body {
margin: 0; padding: 0; overflow: hidden; background: transparent;
display: flex; justify-content: center; align-items: center;
height: 100vh; width: 100vw;
}
img { max-width: 100%; max-height: 100%; object-fit: contain; }
</style>
</head>
<body>
<img id="status-icon" src="/icon" />
<script>
let currentMuted = null;
let currentDeafened = null;

async function updateStatus() {
try {
const response = await fetch('/status?t=' + Date.now());
const data = await response.json();

if (data.muted !== currentMuted || data.deafened !== currentDeafened) {
currentMuted = data.muted;
currentDeafened = data.deafened;
const icon = document.getElementById('status-icon');
icon.src = '/icon?t=' + Date.now();
}
} catch (e) { }
}

setInterval(updateStatus, 500);
updateStatus();
</script>
</body>
</html>
`);
});

server.listen(4697, '0.0.0.0', () => {
console.log('Microphone Status Overlay server running on http://localhost:4697');
});
}

const store = new Store<ISettings>();
app.commandLine.appendSwitch('disable-pinch');

Expand All @@ -43,7 +156,7 @@ if (platform() === 'linux' || !store.get('hardware_acceleration', true)) {

}

if(platform() === 'linux'){
if (platform() === 'linux') {
app.commandLine.appendSwitch('disable-gpu-sandbox');
}

Expand Down Expand Up @@ -304,6 +417,8 @@ if (!gotTheLock) {
callback(pathname);
});

startMicrophoneStatusServer();

protocol.registerFileProtocol('generate', async (request, callback) => {
const url = new URL(request.url.replace('generate:///', ''));
const path = await GenerateHat(url, gameReader.playercolors, Number(url.searchParams.get('color')), '');
Expand Down
10 changes: 9 additions & 1 deletion src/main/ipc-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import path from 'path';
import fs from 'fs';

import { IpcMessages, IpcOverlayMessages } from '../common/ipc-messages';
import { updateMuteStatus } from './index';

// Listeners are fire and forget, they do not have "responses" or return values
export const initializeIpcListeners = (): void => {
Expand Down Expand Up @@ -50,8 +51,15 @@ export const initializeIpcListeners = (): void => {
app.quit();
});

ipcMain.on(IpcMessages.SEND_TO_OVERLAY, (_, event: IpcOverlayMessages, ...args: unknown[]) => {
ipcMain.on(IpcMessages.SEND_TO_OVERLAY, (_, event: IpcOverlayMessages, ...args: any[]) => {
try {
if (event === IpcOverlayMessages.NOTIFY_VOICE_STATE_CHANGED) {
const state = args[0];
if (state) {
const isMuted = state.effectivelyMuted !== undefined ? state.effectivelyMuted : (state.muted || false);
updateMuteStatus(isMuted, state.deafened || false);
}
}
if (global.overlay) global.overlay.webContents.send(event, ...args);
} catch (e) {
/*empty*/
Expand Down
8 changes: 5 additions & 3 deletions src/renderer/Voice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,7 @@ const Voice: React.FC<VoiceProps> = function ({ t, error: initialError }: VoiceP
if (!connectionStuff.current.deafened && !connectionStuff.current.muted) {
inStream.getAudioTracks()[0].enabled =
connectionStuff.current.pushToTalkMode === pushToTalkOptions.PUSH_TO_TALK ? pressing : !pressing;
setTalking(pressing);
}
});

Expand Down Expand Up @@ -1305,9 +1306,7 @@ const Voice: React.FC<VoiceProps> = function ({ t, error: initialError }: VoiceP

// Pass voice state to overlay
useEffect(() => {
if (!settings.enableOverlay) {
return;
}
const isEffectivelyMuted = mutedState || deafenedState || (settings.pushToTalkMode !== pushToTalkOptions.VOICE && !talking);
ipcRenderer.send(IpcMessages.SEND_TO_OVERLAY, IpcOverlayMessages.NOTIFY_VOICE_STATE_CHANGED, {
otherTalking,
playerSocketIds: playerSocketIdsRef.current,
Expand All @@ -1319,6 +1318,7 @@ const Voice: React.FC<VoiceProps> = function ({ t, error: initialError }: VoiceP
impostorRadioClientId: !myPlayer?.isImpostor ? -1 : impostorRadioClientId.current,
muted: mutedState,
deafened: deafenedState,
effectivelyMuted: isEffectivelyMuted,
mod: gameState.mod,
} as VoiceState);
}, [
Expand All @@ -1330,6 +1330,8 @@ const Voice: React.FC<VoiceProps> = function ({ t, error: initialError }: VoiceP
mutedState,
deafenedState,
impostorRadioClientId.current,
gameState.mod,
myPlayer?.isDead
]);

return (
Expand Down
Binary file added static/Muted.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/Unmuted.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.