Initial commit
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ModIdSchema = z.enum([
|
||||
'dxvk',
|
||||
'nampower',
|
||||
'multiMonitorFix',
|
||||
'transmogFix',
|
||||
'unitXp',
|
||||
'vanillaFixes',
|
||||
'vanillaHelpers'
|
||||
]);
|
||||
export type ModId = z.infer<typeof ModIdSchema>;
|
||||
|
||||
export type ModSource =
|
||||
| {
|
||||
kind: 'directFile';
|
||||
url: string;
|
||||
versionUrl?: string;
|
||||
latestVersionUrl?: string;
|
||||
parseLatest?: 'githubRelease' | 'gitlabRelease' | 'codebergRelease';
|
||||
apiUrl?: string;
|
||||
pinnedTag?: string;
|
||||
assetName: string;
|
||||
}
|
||||
| {
|
||||
kind: 'archive';
|
||||
url: string;
|
||||
latestVersionUrl?: string;
|
||||
apiUrl?: string;
|
||||
parseLatest?: 'githubRelease' | 'gitlabRelease' | 'codebergRelease';
|
||||
pinnedTag?: string;
|
||||
format: 'zip' | 'tar.gz';
|
||||
extractMap: Record<string, string>;
|
||||
}
|
||||
| { kind: 'managed' };
|
||||
|
||||
export type ModEntry = {
|
||||
id: ModId;
|
||||
name: string;
|
||||
version: string;
|
||||
description: string;
|
||||
recommended?: boolean;
|
||||
requires?: ModId[];
|
||||
repoUrl: string;
|
||||
source: ModSource;
|
||||
registerInDllsTxt?: string;
|
||||
};
|
||||
|
||||
export const MODS: ModEntry[] = [
|
||||
{
|
||||
id: 'dxvk',
|
||||
name: 'dxvk',
|
||||
version: 'v2.7.1-1',
|
||||
description: 'Enables Vulkan based rendering mode for better performance.',
|
||||
recommended: true,
|
||||
repoUrl: 'https://gitlab.com/Ph42oN/dxvk-gplasync',
|
||||
source: {
|
||||
kind: 'archive',
|
||||
url: 'https://gitlab.com/Ph42oN/dxvk-gplasync/-/raw/main/releases/dxvk-gplasync-v2.7.1-1.tar.gz?ref_type=heads',
|
||||
pinnedTag: 'v2.7.1-1',
|
||||
format: 'tar.gz',
|
||||
extractMap: {
|
||||
'dxvk-gplasync-v2.7.1-1/x32/d3d9.dll': 'd3d9.dll'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'nampower',
|
||||
name: 'nampower',
|
||||
version: 'v4.6.0',
|
||||
description:
|
||||
'A client modification that minimizes your input lag if you have higher latency.',
|
||||
repoUrl: 'https://gitea.com/avitasia/nampower',
|
||||
requires: ['vanillaFixes'],
|
||||
source: {
|
||||
kind: 'directFile',
|
||||
url: 'https://gitea.com/avitasia/nampower/releases/download/v4.6.0/nampower.dll',
|
||||
pinnedTag: 'v4.6.0',
|
||||
assetName: 'nampower.dll'
|
||||
},
|
||||
registerInDllsTxt: 'nampower.dll'
|
||||
},
|
||||
{
|
||||
id: 'multiMonitorFix',
|
||||
name: 'no1600x1200',
|
||||
version: '0.2',
|
||||
description: 'Fix for larger resolutions or multi monitor setups.',
|
||||
repoUrl: 'https://github.com/Mates1500/VanillaMultiMonitorFix',
|
||||
requires: ['vanillaFixes'],
|
||||
source: {
|
||||
kind: 'archive',
|
||||
url: 'https://github.com/Mates1500/VanillaMultiMonitorFix/releases/download/0.2/release.zip',
|
||||
apiUrl:
|
||||
'https://api.github.com/repos/Mates1500/VanillaMultiMonitorFix/releases/latest',
|
||||
parseLatest: 'githubRelease',
|
||||
pinnedTag: '0.2',
|
||||
format: 'zip',
|
||||
extractMap: {
|
||||
'VanillaMultiMonitorFix.dll': 'VanillaMultiMonitorFix.dll'
|
||||
}
|
||||
},
|
||||
registerInDllsTxt: 'VanillaMultiMonitorFix.dll'
|
||||
},
|
||||
{
|
||||
id: 'transmogFix',
|
||||
name: 'transmogFix',
|
||||
version: 'v0.7.0',
|
||||
description:
|
||||
"A client-side fix that eliminates frame drops caused by the server's transmogrification durability workaround.",
|
||||
repoUrl: 'https://codeberg.org/MarcelineVQ/WeirdUtils',
|
||||
requires: ['vanillaFixes'],
|
||||
source: {
|
||||
kind: 'directFile',
|
||||
url: 'https://codeberg.org/MarcelineVQ/WeirdUtils/releases/download/v0.7.0/transmogfix.dll',
|
||||
pinnedTag: 'v0.7.0',
|
||||
assetName: 'transmogfix.dll'
|
||||
},
|
||||
registerInDllsTxt: 'transmogfix.dll'
|
||||
},
|
||||
{
|
||||
id: 'unitXp',
|
||||
name: 'unitXp',
|
||||
version: 'v89',
|
||||
description: 'An attempt to make Vanilla 1.12 modern.',
|
||||
repoUrl: 'https://codeberg.org/konaka/UnitXP_SP3',
|
||||
requires: ['vanillaFixes'],
|
||||
source: {
|
||||
kind: 'archive',
|
||||
url: 'https://codeberg.org/konaka/UnitXP_SP3/releases/download/v89/UnitXP_SP3%20v89.zip',
|
||||
pinnedTag: 'v89',
|
||||
format: 'zip',
|
||||
extractMap: {
|
||||
'UnitXP_SP3.dll': 'UnitXP_SP3.dll'
|
||||
}
|
||||
},
|
||||
registerInDllsTxt: 'UnitXP_SP3.dll'
|
||||
},
|
||||
{
|
||||
id: 'vanillaFixes',
|
||||
name: 'vanillaFixes',
|
||||
version: 'v1.5.3',
|
||||
description: 'A client modification that eliminates stutter and animation lag.',
|
||||
recommended: true,
|
||||
repoUrl: 'https://github.com/hannesmann/vanillafixes',
|
||||
source: {
|
||||
kind: 'archive',
|
||||
url: 'https://github.com/hannesmann/vanillafixes/releases/download/v1.5.3/vanillafixes-1.5.3.zip',
|
||||
apiUrl:
|
||||
'https://api.github.com/repos/hannesmann/vanillafixes/releases/latest',
|
||||
parseLatest: 'githubRelease',
|
||||
pinnedTag: 'v1.5.3',
|
||||
format: 'zip',
|
||||
extractMap: {
|
||||
'VfPatcher.dll': 'VfPatcher.dll',
|
||||
'VanillaFixes.exe': 'VanillaFixes.exe'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'vanillaHelpers',
|
||||
name: 'vanillaHelpers',
|
||||
version: 'v1.1.2',
|
||||
description: 'Utility library that might be required by other patches and addons.',
|
||||
repoUrl: 'https://github.com/isfir/VanillaHelpers',
|
||||
requires: ['vanillaFixes'],
|
||||
source: {
|
||||
kind: 'directFile',
|
||||
url: 'https://github.com/isfir/VanillaHelpers/releases/download/v1.1.2/VanillaHelpers.dll',
|
||||
apiUrl:
|
||||
'https://api.github.com/repos/isfir/VanillaHelpers/releases/latest',
|
||||
parseLatest: 'githubRelease',
|
||||
pinnedTag: 'v1.1.2',
|
||||
assetName: 'VanillaHelpers.dll'
|
||||
},
|
||||
registerInDllsTxt: 'VanillaHelpers.dll'
|
||||
}
|
||||
];
|
||||
|
||||
export const getMod = (id: ModId): ModEntry | undefined =>
|
||||
MODS.find(m => m.id === id);
|
||||
@@ -0,0 +1,110 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
const f = {
|
||||
boolean: (defaultValue?: boolean) =>
|
||||
z.boolean().nullish().default(!!defaultValue),
|
||||
number: (defaultValue?: number, val?: (v: z.ZodNumber) => z.ZodNumber) =>
|
||||
z.preprocess(
|
||||
v =>
|
||||
v === '' || v === undefined
|
||||
? defaultValue ?? null
|
||||
: typeof v === 'string'
|
||||
? Number(v)
|
||||
: v,
|
||||
(val?.(z.number()) ?? z.number()).nullish()
|
||||
)
|
||||
};
|
||||
|
||||
export const ConfigWtfSchema = z.object({
|
||||
vanillaFixes: f.boolean(),
|
||||
largeAddress: f.boolean(true),
|
||||
nameplateRange: f.number(41),
|
||||
alwaysAutoLoot: f.boolean(),
|
||||
fieldOfView: f.number(110),
|
||||
farClip: f.number(777),
|
||||
frillDistance: f.number(70),
|
||||
cameraDistance: f.number(50),
|
||||
soundInBackground: f.boolean(true)
|
||||
});
|
||||
export type ConfigWtfSchema = z.infer<typeof ConfigWtfSchema>;
|
||||
|
||||
export const ModStateSchema = z.object({
|
||||
enabled: z.boolean().default(false),
|
||||
installedVersion: z.string().optional(),
|
||||
installedFiles: z.array(z.string()).default([]),
|
||||
ignoreUpdates: z.boolean().default(false)
|
||||
});
|
||||
export type ModState = z.infer<typeof ModStateSchema>;
|
||||
|
||||
export const PreferencesSchema = z.object({
|
||||
isPortable: z.boolean().optional(),
|
||||
server: z.enum(['live', 'ptr']).default('live'),
|
||||
clientDir: z.string().optional(),
|
||||
version: z.string().optional(),
|
||||
lastPatchedLauncherVersion: z.string().optional(),
|
||||
expectedPatchedWowHash: z.string().optional(),
|
||||
minimizeToTrayOnPlay: f.boolean(true),
|
||||
cleanWdb: f.boolean(),
|
||||
rememberPosition: f.boolean(),
|
||||
windowPosition: z
|
||||
.object({
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
width: z.number(),
|
||||
height: z.number()
|
||||
})
|
||||
.nullish(),
|
||||
config: ConfigWtfSchema.default({}),
|
||||
mods: z.record(ModStateSchema).default({})
|
||||
});
|
||||
export type PreferencesSchema = z.infer<typeof PreferencesSchema>;
|
||||
|
||||
export const TocDataSchema = z.object({
|
||||
Interface: z.string(),
|
||||
Title: z.string(),
|
||||
Author: z.string(),
|
||||
Notes: z.string(),
|
||||
Version: z.string(),
|
||||
Dependencies: z.string().optional(),
|
||||
OptionalDeps: z.string().optional()
|
||||
});
|
||||
|
||||
export type TocData = z.infer<typeof TocDataSchema>;
|
||||
|
||||
export const AddonDataSchema = z.object({
|
||||
status: z.enum([
|
||||
'available',
|
||||
'fetching',
|
||||
'unknown',
|
||||
'upToDate',
|
||||
'outOfDate',
|
||||
'downloading',
|
||||
'invalid'
|
||||
]),
|
||||
git: z.string().optional(),
|
||||
toc: TocDataSchema.optional(),
|
||||
description: z.string().optional(),
|
||||
error: z.string().optional(),
|
||||
branch: z.string().optional(),
|
||||
ref: z.string().optional(),
|
||||
folder: z.string(),
|
||||
progress: z.string().optional(),
|
||||
preview: z.string().optional()
|
||||
});
|
||||
|
||||
export type AddonData = z.infer<typeof AddonDataSchema>;
|
||||
|
||||
export const NewsItemSchema = z.object({
|
||||
id: z.string(),
|
||||
title: z.string(),
|
||||
date: z.string(),
|
||||
body: z.string(),
|
||||
url: z.string().url().optional(),
|
||||
author: z.string().optional()
|
||||
});
|
||||
export type NewsItem = z.infer<typeof NewsItemSchema>;
|
||||
|
||||
export const NewsFeedSchema = z.object({
|
||||
items: z.array(NewsItemSchema)
|
||||
});
|
||||
export type NewsFeed = z.infer<typeof NewsFeedSchema>;
|
||||
@@ -0,0 +1,74 @@
|
||||
type Path = readonly (string | number)[];
|
||||
|
||||
export const nestedGet = <T>(object: unknown, path: Path) =>
|
||||
path.reduce((obj, key) => obj?.[key], object) as T;
|
||||
|
||||
export const nestedSet = (obj: any, path: Path, value: unknown) => {
|
||||
const [key, ...rest] = path;
|
||||
if (path.length === 1) {
|
||||
obj[key] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj[key] === undefined) {
|
||||
obj[key] = typeof rest[0] === 'number' ? [] : {};
|
||||
}
|
||||
|
||||
nestedSet(obj[key], rest as never, value);
|
||||
};
|
||||
|
||||
export const asyncReduce = async <T, U>(
|
||||
arr: T[],
|
||||
reducer: (acc: U, cur: T) => Promise<U>,
|
||||
init: U
|
||||
): Promise<U> => {
|
||||
let acc: U = init;
|
||||
for (const i of arr) acc = await reducer(acc, i);
|
||||
return acc;
|
||||
};
|
||||
|
||||
export const asyncMap = async <T, U>(
|
||||
arr: T[],
|
||||
map: (cur: T) => Promise<U>
|
||||
): Promise<U[]> => {
|
||||
const acc: U[] = [];
|
||||
for (const i of arr) acc.push(await map(i));
|
||||
return acc;
|
||||
};
|
||||
|
||||
export const isNotUndef = <T>(obj: T): obj is Exclude<T, undefined> =>
|
||||
obj !== undefined;
|
||||
|
||||
export const formatFileSize = (bytes: number) => {
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
let size = bytes;
|
||||
let unitIndex = 0;
|
||||
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
||||
};
|
||||
|
||||
export const formatDuration = (remaining: number) => {
|
||||
const hours = Math.floor(remaining / 3600);
|
||||
const minutes = Math.floor((remaining % 3600) / 60);
|
||||
const seconds = Math.floor(remaining % 60);
|
||||
|
||||
return `${hours ? `${hours}h ` : ''}${
|
||||
minutes ? `${minutes}m ` : ''
|
||||
}${seconds}s`;
|
||||
};
|
||||
|
||||
export const omit = <T extends object, const K extends keyof T>(
|
||||
obj: T,
|
||||
keys: K[]
|
||||
): Omit<T, K> => {
|
||||
const result = { ...obj };
|
||||
keys.forEach(key => {
|
||||
delete result[key];
|
||||
});
|
||||
return result;
|
||||
};
|
||||
Reference in New Issue
Block a user