mirror of
https://github.com/lexogrine/cs2-react-hud.git
synced 2026-05-04 12:13:11 +02:00
Updated setup to vite and moved to hooks instead of class
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
import { Instance, SignalData } from 'simple-peer';
|
||||
import api from '..';
|
||||
import { socket } from '../socket';
|
||||
import Peer from 'simple-peer';
|
||||
|
||||
const wait = (ms: number) => new Promise(r => setTimeout(r, ms));
|
||||
|
||||
type OfferData = {
|
||||
offer: SignalData,
|
||||
}
|
||||
|
||||
type PeerInstance = Instance & { _remoteStreams: MediaStream[] }
|
||||
|
||||
type MediaStreamPlayer = {
|
||||
peerConnection: PeerInstance | null;
|
||||
steamid: string;
|
||||
}
|
||||
|
||||
type ListenerType = ({ listener: (stream: MediaStream) => void, event: 'create', steamid: string } | ({ listener: () => void, event: 'destroy', steamid: string }));
|
||||
|
||||
type MediaStreamManager = {
|
||||
blocked: string[];
|
||||
blockedListeners: ((blocked: string[]) => void)[],
|
||||
players: MediaStreamPlayer[];
|
||||
onStreamCreate: (listener: (stream: MediaStream) => void, steamid: string) => void;
|
||||
onStreamDestroy: (listener: () => void, steamid: string) => void;
|
||||
onBlockedUpdate: (listener: (steamids: string[]) => void) => void;
|
||||
removeListener: (listener: any) => void;
|
||||
listeners: ListenerType[];
|
||||
}
|
||||
|
||||
const mediaStreams: MediaStreamManager = {
|
||||
blocked: [],
|
||||
blockedListeners: [],
|
||||
players: [],
|
||||
listeners: [],
|
||||
onStreamCreate: (listener: (stream: MediaStream) => void, steamid: string) => {
|
||||
mediaStreams.listeners.push({ listener, event: "create", steamid });
|
||||
},
|
||||
onBlockedUpdate: (listener: (blocked: string[]) => void) => {
|
||||
mediaStreams.blockedListeners.push(listener);
|
||||
},
|
||||
onStreamDestroy: (listener: () => void, steamid: string) => {
|
||||
mediaStreams.listeners.push({ listener, event: "destroy", steamid });
|
||||
},
|
||||
removeListener: (listenerToRemove: any) => {
|
||||
mediaStreams.listeners = mediaStreams.listeners.filter(listener => listener !== listenerToRemove);
|
||||
mediaStreams.blockedListeners = mediaStreams.blockedListeners.filter(listener => listener !== listenerToRemove);
|
||||
}
|
||||
};
|
||||
|
||||
const getConnectionInfo = (steamid: string) => mediaStreams.players.find(player => player.steamid === steamid) || null;
|
||||
|
||||
const closeConnection = (steamid: string) => {
|
||||
const connectionInfo = getConnectionInfo(steamid);
|
||||
try {
|
||||
if (connectionInfo) {
|
||||
if (connectionInfo.peerConnection) {
|
||||
connectionInfo.peerConnection.removeAllListeners();
|
||||
connectionInfo.peerConnection.destroy();
|
||||
}
|
||||
connectionInfo.peerConnection = null;
|
||||
}
|
||||
} catch {
|
||||
|
||||
}
|
||||
|
||||
for (const listener of mediaStreams.listeners.filter(listener => listener.steamid === steamid)) {
|
||||
if (listener.event === "destroy") listener.listener();
|
||||
}
|
||||
mediaStreams.players = mediaStreams.players.filter(player => player.steamid !== steamid);
|
||||
console.log(mediaStreams.players)
|
||||
}
|
||||
|
||||
const initiateConnection = async () => {
|
||||
const camera = await api.camera.get();
|
||||
await wait(1000);
|
||||
|
||||
socket.emit("registerAsHUD", camera.uuid);
|
||||
|
||||
socket.on('playersCameraStatus', (players: { steamid: string, label: string, allow: boolean, active: boolean }[]) => {
|
||||
const blockedSteamids = players.filter(player => !player.allow).map(player => player.steamid);
|
||||
mediaStreams.blocked = blockedSteamids;
|
||||
|
||||
for (const listener of mediaStreams.blockedListeners) {
|
||||
listener(blockedSteamids);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('offerFromPlayer', async (roomId: string, offerData: OfferData, steamid: string) => {
|
||||
// It's not from available player, ignore incoming request
|
||||
/*if(!camera.availablePlayers.find(player => player.steamid === steamid)){
|
||||
console.log("Wrong player");
|
||||
return;
|
||||
}*/
|
||||
const currentConnection = getConnectionInfo(steamid);
|
||||
|
||||
// Connection already made, ignore incoming request
|
||||
if (currentConnection) {
|
||||
console.log("Connection has been made already");
|
||||
return;
|
||||
}
|
||||
|
||||
if (camera.uuid !== roomId) return;
|
||||
|
||||
const peerConnection = new Peer({ initiator: false, trickle: false }) as PeerInstance;
|
||||
|
||||
const mediaStreamPlayer: MediaStreamPlayer = { peerConnection, steamid };
|
||||
|
||||
mediaStreams.players.push(mediaStreamPlayer);
|
||||
|
||||
peerConnection.on('signal', answer => {
|
||||
console.log("SIGNAL COMING IN");
|
||||
const offer = JSON.parse(JSON.stringify(answer)) as RTCSessionDescriptionInit;
|
||||
socket.emit("offerFromHUD", roomId, { offer }, steamid);
|
||||
});
|
||||
|
||||
peerConnection.on('error', (err) => {
|
||||
console.log(err)
|
||||
closeConnection(steamid);
|
||||
});
|
||||
|
||||
peerConnection.on('stream', () => {
|
||||
console.log("STREAM COMING IN");
|
||||
const currentConnection = getConnectionInfo(steamid);
|
||||
if (!currentConnection) {
|
||||
console.log("Connection not established");
|
||||
closeConnection(steamid);
|
||||
return;
|
||||
}
|
||||
if (peerConnection._remoteStreams.length === 0) {
|
||||
console.log('no stream?');
|
||||
return;
|
||||
}
|
||||
for (const listener of mediaStreams.listeners.filter(listener => listener.steamid === steamid)) {
|
||||
if (listener.event === "create") listener.listener(peerConnection._remoteStreams[0]);
|
||||
}
|
||||
});
|
||||
|
||||
peerConnection.on('close', () => {
|
||||
console.log("CLOSE COMING IN");
|
||||
const currentConnection = getConnectionInfo(steamid);
|
||||
if (!currentConnection) return;
|
||||
|
||||
closeConnection(steamid);
|
||||
});
|
||||
console.log("Sending offer");
|
||||
peerConnection.signal(offerData.offer);
|
||||
});
|
||||
}
|
||||
|
||||
export { mediaStreams, initiateConnection };
|
||||
@@ -0,0 +1,88 @@
|
||||
import { CSGOGSI, Player, PlayerExtension } from 'csgogsi';
|
||||
import api, { isDev } from '..';
|
||||
|
||||
export const hudIdentity = {
|
||||
name: '',
|
||||
isDev
|
||||
};
|
||||
|
||||
export const GSI = new CSGOGSI();
|
||||
GSI.regulationMR = 12;
|
||||
|
||||
GSI.on("data", data => {
|
||||
loadPlayers(data.players);
|
||||
});
|
||||
|
||||
const requestedSteamIDs: string[] = [];
|
||||
|
||||
const loadPlayers = async (players: Player[]) => {
|
||||
const leftOvers = players.filter(player => !requestedSteamIDs.includes(player.steamid));
|
||||
const leftOverSteamids = leftOvers.map(player => player.steamid);
|
||||
if(!leftOvers.length) return;
|
||||
|
||||
requestedSteamIDs.push(...leftOverSteamids);
|
||||
|
||||
const extensions = await api.players.get(leftOverSteamids);
|
||||
|
||||
const playersExtensions: PlayerExtension[] = extensions.map(player => (
|
||||
{
|
||||
id: player._id,
|
||||
name: player.username,
|
||||
realName: `${player.firstName} ${player.lastName}`,
|
||||
steamid: player.steamid,
|
||||
country: player.country,
|
||||
avatar: player.avatar,
|
||||
extra: player.extra,
|
||||
})
|
||||
)
|
||||
|
||||
GSI.players.push(...playersExtensions);
|
||||
|
||||
|
||||
leftOvers.forEach(player => {
|
||||
loadAvatarURL(player);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
interface AvatarLoader {
|
||||
loader: Promise<string>,
|
||||
url: string,
|
||||
}
|
||||
|
||||
const avatars: { [key: string]: AvatarLoader } = {};
|
||||
|
||||
const loadAvatarURL = (player: Player) => {
|
||||
if(!player.steamid) return;
|
||||
if(avatars[player.steamid]) return avatars[player.steamid].url;
|
||||
avatars[player.steamid] = {
|
||||
url: player.avatar || '',
|
||||
loader: new Promise((resolve) => {
|
||||
api.players.getAvatarURLs(player.steamid).then(result => {
|
||||
const avatarUrl = result.custom || result.steam;
|
||||
const existing = GSI.players.find(playerEx => playerEx.steamid === player.steamid);
|
||||
const target = existing || {
|
||||
id: player.steamid,
|
||||
name: player.name,
|
||||
realName: player.realName,
|
||||
steamid: player.steamid,
|
||||
country: player.country,
|
||||
avatar: player.avatar,
|
||||
extra: player.extra
|
||||
}
|
||||
if(target) target.avatar = avatarUrl;
|
||||
|
||||
if(!existing){
|
||||
GSI.players.push(target);
|
||||
}
|
||||
|
||||
|
||||
avatars[player.steamid].url = result.custom || result.steam;
|
||||
resolve(result.custom || result.custom);
|
||||
}).catch(() => {
|
||||
delete avatars[player.steamid];
|
||||
resolve('');
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import ActionManager, { ActionHandler, ConfigManager } from "./managers";
|
||||
import { Events } from "csgogsi";
|
||||
import { GSI } from "../HUD";
|
||||
import type { AllActions, GetInputsFromSection, Sections } from "./settings";
|
||||
|
||||
export const actions = new ActionManager();
|
||||
export const configs = new ConfigManager();
|
||||
|
||||
type EmptyListener = () => void;
|
||||
|
||||
type BaseEvents = keyof Events;
|
||||
type Callback<K> = K extends BaseEvents ? Events[K] | EmptyListener : EmptyListener;
|
||||
|
||||
export function useAction<T extends keyof AllActions>(action: T, callback: ActionHandler<T extends keyof AllActions ? AllActions[T] : never>, deps?: React.DependencyList) {
|
||||
useEffect(() => {
|
||||
|
||||
actions.on(action, callback);
|
||||
return () => {
|
||||
actions.off(action, callback);
|
||||
};
|
||||
}, deps ? [action, ...deps] : [action, callback]);
|
||||
return null;
|
||||
}
|
||||
export function useOnConfigChange<K extends keyof Sections, T = any>(section: K, callback: ActionHandler<{ [L in keyof (K extends keyof Sections ? GetInputsFromSection<Sections[K]> : T)]?: (K extends keyof Sections ? GetInputsFromSection<Sections[K]> : T)[L] } | null>, deps?: React.DependencyList){
|
||||
|
||||
useEffect(() => {
|
||||
const onDataChanged = (data: any) => {
|
||||
callback(data?.[section] || null);
|
||||
};
|
||||
|
||||
configs.onChange(onDataChanged);
|
||||
onDataChanged(configs.data);
|
||||
|
||||
return () => {
|
||||
configs.off(onDataChanged);
|
||||
}
|
||||
}, deps ? [section, ...deps] : [section, callback])
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function onGSI<T extends BaseEvents>(event: T, callback: Callback<T>, deps?: React.DependencyList){
|
||||
useEffect(() => {
|
||||
GSI.on(event, callback);
|
||||
|
||||
return () => {
|
||||
GSI.off(event, callback);
|
||||
}
|
||||
}, deps ? [event, ...deps] : [event, callback])
|
||||
}
|
||||
|
||||
export function useConfig<K extends keyof Sections, T extends { [K: string]: any } = {}>(section: K){
|
||||
const [ data, setData ] = useState<{ [L in keyof (K extends keyof Sections ? GetInputsFromSection<Sections[K]> : T)]?: (K extends keyof Sections ? GetInputsFromSection<Sections[K]> : T)[L] } | null>(configs.data?.[section] || null);
|
||||
|
||||
const onDataChanged = useCallback((sectionData: any) => {
|
||||
setData(sectionData || null);
|
||||
}, [section]);
|
||||
|
||||
useOnConfigChange(section, onDataChanged);
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
export const keybindDefinition = [
|
||||
{
|
||||
"bind": "Alt+C",
|
||||
"action": "toggleCams"
|
||||
},
|
||||
{
|
||||
"bind": "Alt+V",
|
||||
"action": "radarBigger"
|
||||
},
|
||||
{
|
||||
"bind": "Alt+B",
|
||||
"action": "radarSmaller"
|
||||
}
|
||||
] as const;
|
||||
@@ -0,0 +1,71 @@
|
||||
export type ActionHandler<T = any> = (data: T) => void;
|
||||
|
||||
export default class ActionManager {
|
||||
handlers: { [K in string]?: ActionHandler[] };
|
||||
|
||||
constructor(){
|
||||
this.handlers = {}
|
||||
|
||||
/*this.on('data', _data => {
|
||||
});*/
|
||||
}
|
||||
execute = <T = any>(eventName: string, argument?: T) => {
|
||||
const handlers = this.handlers[eventName] || [];
|
||||
for(const handler of handlers){
|
||||
handler(argument);
|
||||
}
|
||||
}
|
||||
|
||||
on = <T = any>(eventName: string, handler: ActionHandler<T>) => {
|
||||
if(!this.handlers[eventName]) this.handlers[eventName] = [];
|
||||
this.handlers[eventName]!.push(handler);
|
||||
}
|
||||
|
||||
off = (eventName: string, handler: ActionHandler) => {
|
||||
if(!this.handlers[eventName]) this.handlers[eventName] = [];
|
||||
this.handlers[eventName] = this.handlers[eventName]!.filter(h => h !== handler);
|
||||
}
|
||||
}
|
||||
export class ConfigManager {
|
||||
listeners: ActionHandler[];
|
||||
data: { [K in string]?: any };
|
||||
|
||||
constructor(){
|
||||
this.listeners = [];
|
||||
this.data = {};
|
||||
}
|
||||
save(data: { [K in string]?: any }){
|
||||
this.data = data;
|
||||
this.execute();
|
||||
|
||||
/*const listeners = this.listeners.get(eventName);
|
||||
if(!listeners) return false;
|
||||
listeners.forEach(callback => {
|
||||
if(argument) callback(argument);
|
||||
else callback();
|
||||
});
|
||||
return true;*/
|
||||
}
|
||||
|
||||
execute(){
|
||||
const listeners = this.listeners;
|
||||
if(!listeners || !listeners.length) return false;
|
||||
listeners.forEach(listener => {
|
||||
listener(this.data);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
onChange = (listener: ActionHandler) => {
|
||||
const listOfListeners = this.listeners || [];
|
||||
listOfListeners.push(listener);
|
||||
this.listeners = listOfListeners;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
off = (listener: ActionHandler) => {
|
||||
this.listeners = this.listeners.filter(l => l !== listener);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
export const panelDefinition = [
|
||||
{
|
||||
"label": "Trivia",
|
||||
"name": "trivia",
|
||||
"inputs": [
|
||||
{
|
||||
"type": "text",
|
||||
"name": "title",
|
||||
"label": "Trivia title"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"name": "content",
|
||||
"label": "Trivia content"
|
||||
},
|
||||
{
|
||||
"type": "action",
|
||||
"name": "triviaState",
|
||||
"values": [
|
||||
{
|
||||
"name": "show",
|
||||
"label": "Show trivia"
|
||||
},
|
||||
{
|
||||
"name": "hide",
|
||||
"label": "Hide trivia"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Display settings",
|
||||
"name": "display_settings",
|
||||
"inputs": [
|
||||
{
|
||||
"type": "text",
|
||||
"name": "left_title",
|
||||
"label": "Left box's title"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"name": "right_title",
|
||||
"label": "Right box's title"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"name": "left_subtitle",
|
||||
"label": "Left box's subtitle"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"name": "right_subtitle",
|
||||
"label": "Right box's subtitle"
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "left_image",
|
||||
"label": "Left box's image logo"
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"name": "right_image",
|
||||
"label": "Right box's image logo"
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"name": "replace_avatars",
|
||||
"label": "Use team logos as player avatars",
|
||||
"values": [
|
||||
{
|
||||
"label": "Only if player has no avatar",
|
||||
"name": "if_missing"
|
||||
},
|
||||
{
|
||||
"label": "Always",
|
||||
"name": "always"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "action",
|
||||
"name": "boxesState",
|
||||
"values": [
|
||||
{
|
||||
"name": "show",
|
||||
"label": "Show boxes"
|
||||
},
|
||||
{
|
||||
"name": "hide",
|
||||
"label": "Hide boxes"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "action",
|
||||
"name": "toggleRadarView",
|
||||
"values": [
|
||||
{
|
||||
"name": "toggler",
|
||||
"label": "Toggle radar view"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Player & Match overview",
|
||||
"name": "preview_settings",
|
||||
"inputs": [
|
||||
{
|
||||
"type": "match",
|
||||
"name": "match_preview",
|
||||
"label": "Pick an upcoming match"
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"name": "select_preview",
|
||||
"label": "Mood indicator",
|
||||
"values": [
|
||||
{
|
||||
"name": "show",
|
||||
"label": ":)"
|
||||
},
|
||||
{
|
||||
"name": "hide",
|
||||
"label": ":("
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "player",
|
||||
"name": "player_preview",
|
||||
"label": "Pick a player to preview"
|
||||
},
|
||||
{
|
||||
"type": "checkbox",
|
||||
"name": "player_preview_toggle",
|
||||
"label": "Show player preview"
|
||||
},
|
||||
{
|
||||
"type": "checkbox",
|
||||
"name": "match_preview_toggle",
|
||||
"label": "Show upcoming match"
|
||||
},
|
||||
{
|
||||
"type": "action",
|
||||
"name": "showTournament",
|
||||
"values": [
|
||||
{
|
||||
"name": "show",
|
||||
"label": "Show tournament"
|
||||
},
|
||||
{
|
||||
"name": "hide",
|
||||
"label": "Hide tournament"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
] as const;
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Match, Player, Team } from "../types"
|
||||
import { keybindDefinition } from "./keybinds";
|
||||
import { panelDefinition } from "./panel";
|
||||
|
||||
type Prettify<T> = {
|
||||
[K in keyof T]: T[K];
|
||||
} & {};
|
||||
|
||||
type Settings = typeof panelDefinition;
|
||||
|
||||
type Keybinds = typeof keybindDefinition;
|
||||
|
||||
|
||||
type InputMapper = {
|
||||
text: string;
|
||||
player: {
|
||||
type: "player",
|
||||
id: string,
|
||||
player: Player | null
|
||||
};
|
||||
match: {
|
||||
type: "match",
|
||||
id: string,
|
||||
match: Match | null
|
||||
};
|
||||
team: {
|
||||
type: "team",
|
||||
id: string,
|
||||
team: Team | null
|
||||
};
|
||||
checkbox: boolean;
|
||||
action: never;
|
||||
image: string;
|
||||
images: string[];
|
||||
select: string;
|
||||
}
|
||||
type NonNeverKeys<T extends { [K: string]: any }> = { [K in keyof T]: T[K] extends never ? never : K }[keyof T];
|
||||
|
||||
|
||||
type ValueMapper<T extends Settings[number]["inputs"]> = { [K in T[number] as K["name"]]: K extends { type: "action" } ? never : (K extends { type: "select" } ? K["values"][number]["name"] | "" : InputMapper[K["type"]]) }
|
||||
|
||||
export type GetInputsFromSection<T extends Settings[number]["inputs"]> = { [K in NonNeverKeys<ValueMapper<T>>]: ValueMapper<T>[K]};
|
||||
export type Sections = { [K in Settings[number] as K["name"]]: K["inputs"]}
|
||||
|
||||
type ActionValueMapper<T extends Settings[number]["inputs"]> = { [K in T[number] as K["name"]]: K extends { type: "action" } ? K["values"][number]["name"] : never }
|
||||
|
||||
type GetActionsFromSection<T extends Settings[number]["inputs"]> = { [K in NonNeverKeys<ActionValueMapper<T>>]: ActionValueMapper<T>[K]};
|
||||
|
||||
export type AllActions = Prettify<GetActionsFromSection<Settings[number]["inputs"]> & { [K in Keybinds[number]["action"]]: never }>;
|
||||
@@ -0,0 +1,69 @@
|
||||
import * as I from './types';
|
||||
import { MapConfig } from '../HUD/Radar/LexoRadar/maps';
|
||||
|
||||
|
||||
const query = new URLSearchParams(window.location.search);
|
||||
export const port = Number(query.get('port') || 1349);
|
||||
export const variant = query.get("variant") || "default";
|
||||
|
||||
export const isDev = !query.get("isProd");
|
||||
|
||||
export const config = {apiAddress:isDev ? `http://localhost:${port}/` : '/'}
|
||||
export const apiUrl = config.apiAddress;
|
||||
|
||||
export async function apiV2(url: string, method = 'GET', body?: any) {
|
||||
const options: RequestInit = {
|
||||
method,
|
||||
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
|
||||
}
|
||||
if (body) {
|
||||
options.body = JSON.stringify(body)
|
||||
}
|
||||
let data: any = null;
|
||||
return fetch(`${apiUrl}api/${url}`, options)
|
||||
.then(res => {
|
||||
data = res;
|
||||
return res.json().catch(_e => data && data.status < 300)
|
||||
});
|
||||
}
|
||||
|
||||
const api = {
|
||||
match: {
|
||||
get: async (): Promise<I.Match[]> => apiV2(`match`),
|
||||
getCurrent: async (): Promise<I.Match> => apiV2(`match/current`)
|
||||
},
|
||||
camera: {
|
||||
get: (): Promise<{ availablePlayers: ({steamid:string, label: string})[], uuid: string }> => apiV2('camera'),
|
||||
toggleVmix: (status?: boolean) => new Promise<boolean>(r => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
// let finished = false;
|
||||
const timeoutId = setTimeout(() => {
|
||||
controller.abort();
|
||||
r(false);
|
||||
}, 1000)
|
||||
fetch(`http://localhost:2715/visibility${status !== undefined ? `?status=${status}` : ''}`, { method: "POST", signal }).then(() => {
|
||||
clearTimeout(timeoutId);
|
||||
r(true);
|
||||
//finished = true;
|
||||
}).catch(() => { r(false); });
|
||||
|
||||
})
|
||||
},
|
||||
teams: {
|
||||
getOne: async (id: string): Promise<I.Team> => apiV2(`teams/${id}`),
|
||||
get: (): Promise<I.Team[]> => apiV2(`teams`),
|
||||
},
|
||||
players: {
|
||||
get: async (steamids?: string[]): Promise<I.Player[]> => apiV2(steamids ? `players?steamids=${steamids.join(';')}` :`players`),
|
||||
getAvatarURLs: async (steamid: string): Promise<{custom: string, steam: string}> => apiV2(`players/avatar/steamid/${steamid}`)
|
||||
},
|
||||
tournaments: {
|
||||
get: () => apiV2('tournament')
|
||||
},
|
||||
maps: {
|
||||
get: (): Promise<{ [key: string] : MapConfig}> => apiV2('radar/maps')
|
||||
}
|
||||
}
|
||||
|
||||
export default api;
|
||||
@@ -0,0 +1,69 @@
|
||||
import { io } from "socket.io-client";
|
||||
import { isDev, port } from ".";
|
||||
import { GSI, hudIdentity } from "./HUD";
|
||||
import { CSGORaw } from "csgogsi";
|
||||
import { actions, configs } from "./contexts/actions";
|
||||
import { initiateConnection } from "./HUD/camera";
|
||||
|
||||
export const socket = io(isDev ? `localhost:${port}` : '/');
|
||||
|
||||
type RoundPlayerDamage = {
|
||||
steamid: string;
|
||||
damage: number;
|
||||
};
|
||||
type RoundDamage = {
|
||||
round: number;
|
||||
players: RoundPlayerDamage[];
|
||||
};
|
||||
|
||||
socket.on("update", (data: any, damage: any) => {
|
||||
if (damage) {
|
||||
GSI.damage = damage;
|
||||
}
|
||||
GSI.digest(data);
|
||||
});
|
||||
|
||||
const isInWindow = !!window.parent.ipcApi;
|
||||
|
||||
if(isInWindow){
|
||||
window.parent.ipcApi.receive('raw', (data: CSGORaw, damage?: RoundDamage[]) => {
|
||||
if(damage){
|
||||
GSI.damage = damage;
|
||||
}
|
||||
GSI.digest(data);
|
||||
});
|
||||
}
|
||||
|
||||
const href = window.location.href;
|
||||
|
||||
socket.emit("started");
|
||||
|
||||
if (isDev) {
|
||||
hudIdentity.name = (Math.random() * 1000 + 1).toString(36).replace(/[^a-z]+/g, '').substr(0, 15);
|
||||
hudIdentity.isDev = true;
|
||||
} else {
|
||||
const segment = href.substr(href.indexOf('/huds/') + 6);
|
||||
hudIdentity.name = segment.substr(0, segment.lastIndexOf('/'));
|
||||
}
|
||||
|
||||
socket.on("readyToRegister", () => {
|
||||
socket.emit("register", hudIdentity.name, isDev, "cs2", isInWindow ? "IPC" : "DEFAULT");
|
||||
initiateConnection();
|
||||
});
|
||||
socket.on(`hud_config`, (data: any) => {
|
||||
configs.save(data);
|
||||
});
|
||||
socket.on(`hud_action`, (data: any) => {
|
||||
actions.execute(data.action, data.data);
|
||||
});
|
||||
socket.on('keybindAction', (action: string) => {
|
||||
actions.execute(action);
|
||||
});
|
||||
|
||||
socket.on("refreshHUD", () => {
|
||||
window.top?.location.reload();
|
||||
});
|
||||
|
||||
socket.on("update_mirv", (data: any) => {
|
||||
GSI.digestMIRV(data);
|
||||
})
|
||||
@@ -0,0 +1,161 @@
|
||||
export interface Player {
|
||||
_id: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
username: string;
|
||||
avatar: string;
|
||||
country: string;
|
||||
steamid: string;
|
||||
team: string;
|
||||
extra: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface Team {
|
||||
_id: string;
|
||||
name: string;
|
||||
country: string;
|
||||
shortName: string;
|
||||
logo: string;
|
||||
extra: Record<string, string>;
|
||||
}
|
||||
/*
|
||||
export interface HUD {
|
||||
name: string,
|
||||
version: string,
|
||||
author: string,
|
||||
legacy: boolean,
|
||||
dir: string
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
port: number,
|
||||
steamApiKey: string,
|
||||
token: string,
|
||||
}*/
|
||||
export interface TournamentMatchup {
|
||||
_id: string;
|
||||
loser_to: string | null; // IDs of Matchups, not Matches
|
||||
winner_to: string | null;
|
||||
label: string;
|
||||
matchId: string | null;
|
||||
parents: TournamentMatchup[];
|
||||
}
|
||||
|
||||
export interface DepthTournamentMatchup extends TournamentMatchup {
|
||||
depth: number;
|
||||
parents: DepthTournamentMatchup[];
|
||||
}
|
||||
|
||||
export type TournamentTypes = 'swiss' | 'single' | 'double';
|
||||
|
||||
export type TournamentStage = {
|
||||
type: TournamentTypes;
|
||||
matchups: TournamentMatchup[];
|
||||
teams: number;
|
||||
phases: number;
|
||||
participants: string[];
|
||||
};
|
||||
export interface Tournament {
|
||||
_id: string;
|
||||
name: string;
|
||||
logo: string;
|
||||
groups: TournamentStage[];
|
||||
playoffs: TournamentStage;
|
||||
autoCreate: boolean;
|
||||
}
|
||||
export interface RoundData {
|
||||
round: number,
|
||||
players: {
|
||||
[steamid: string]: PlayerRoundData
|
||||
},
|
||||
winner: 'CT' | 'T' | null,
|
||||
win_type: 'bomb' | 'elimination' | 'defuse' | 'time',
|
||||
}
|
||||
|
||||
export interface PlayerRoundData {
|
||||
kills: number,
|
||||
killshs: number,
|
||||
damage: number,
|
||||
}
|
||||
|
||||
export interface Veto {
|
||||
teamId: string;
|
||||
mapName: string;
|
||||
side: "CT" | "T" | "NO";
|
||||
type: "ban" | "pick" | "decider";
|
||||
reverseSide?: boolean;
|
||||
rounds?: (RoundData | null)[],
|
||||
score?: {
|
||||
[key: string]: number;
|
||||
};
|
||||
winner?: string;
|
||||
mapEnd: boolean;
|
||||
}
|
||||
|
||||
export interface Match {
|
||||
id: string;
|
||||
current: boolean;
|
||||
left: {
|
||||
id: string | null;
|
||||
wins: number;
|
||||
};
|
||||
right: {
|
||||
id: string | null;
|
||||
wins: number;
|
||||
};
|
||||
matchType: "bo1" | "bo2" | "bo3" | "bo5";
|
||||
vetos: Veto[];
|
||||
}
|
||||
|
||||
export type Weapon =
|
||||
| "ak47"
|
||||
| "aug"
|
||||
| "awp"
|
||||
| "bizon"
|
||||
| "famas"
|
||||
| "g3sg1"
|
||||
| "galilar"
|
||||
| "m4a1"
|
||||
| "m4a1_silencer"
|
||||
| "m249"
|
||||
| "mac10"
|
||||
| "mag7"
|
||||
| "mp5sd"
|
||||
| "mp7"
|
||||
| "mp9"
|
||||
| "negev"
|
||||
| "nova"
|
||||
| "p90"
|
||||
| "sawedoff"
|
||||
| "scar20"
|
||||
| "sg556"
|
||||
| "ssg08"
|
||||
| "ump45"
|
||||
| "xm1014"
|
||||
| Pistol
|
||||
| Knife;
|
||||
|
||||
export type Pistol = "c75a" | "deagle" | "elite" | "fiveseven" | "glock" | "hkp2000" | "p250" | "revolver" | "taser" | "tec9" | "usp_silencer";
|
||||
|
||||
export type Knife =
|
||||
| "knife"//
|
||||
| "knife_css"//--
|
||||
| "knife_butterfly"//
|
||||
| "knife_falchion"//
|
||||
| "knife_flip"//
|
||||
| "knife_outdoor" // Nomad Knife
|
||||
| "knife_gut"//
|
||||
| "knife_gypsy_jackknife"//
|
||||
| "knife_karambit"//
|
||||
| "knife_bayonet" //
|
||||
| "knife_cord" //
|
||||
| "knife_m9_bayonet"//
|
||||
| "knife_push" // Shadow daggers
|
||||
| "knife_stiletto"//
|
||||
| "knife_survival_bowie"//
|
||||
| "knife_t"//
|
||||
| "knife_skeleton" //
|
||||
| "knife_tactical"//
|
||||
| "knife_ursus"//
|
||||
| "knife_widowmaker"//
|
||||
| "knife_canis";//
|
||||
Reference in New Issue
Block a user