Initial commit

This commit is contained in:
2026-05-08 00:00:00 +00:00
commit 530ec7a144
110 changed files with 18537 additions and 0 deletions
+180
View File
@@ -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);
+110
View File
@@ -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>;
+74
View File
@@ -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;
};