mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-22 07:07:05 +00:00
Move brand images to proprietary/ and support multi-dir asset pipeline (#3662)
## Description: * Move proprietary brand images (logos, favicon) from resources/images/ to proprietary/images/ to separate open-source assets from proprietary ones * Extend the asset pipeline (PublicAssetManifest, vite.config.ts) to support multiple source directories (resources/ + proprietary/), so buildAssetUrl resolves assets from either location transparently * In dev, serve proprietary/ as a fallback middleware (registered after Vite's publicDir handler) so resources/ takes precedence when files exist in both. The idea is we could have placeholder assets placeholders that can be used by forks, and only the production build uses proprietary assets. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: evan
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
import { Howl } from "howler";
|
||||
import of4 from "../../../proprietary/sounds/music/of4.mp3";
|
||||
import openfront from "../../../proprietary/sounds/music/openfront.mp3";
|
||||
import war from "../../../proprietary/sounds/music/war.mp3";
|
||||
import { assetUrl } from "../../core/AssetUrls";
|
||||
import { EventBus } from "../../core/EventBus";
|
||||
import { UserSettings } from "../../core/game/UserSettings";
|
||||
import {
|
||||
@@ -33,19 +31,19 @@ export class SoundManager {
|
||||
this.safely("initialize background music", () => {
|
||||
this.backgroundMusic = [
|
||||
new Howl({
|
||||
src: [of4],
|
||||
src: [assetUrl("sounds/music/of4.mp3")],
|
||||
loop: false,
|
||||
onend: this.playNext.bind(this),
|
||||
volume: 0,
|
||||
}),
|
||||
new Howl({
|
||||
src: [openfront],
|
||||
src: [assetUrl("sounds/music/openfront.mp3")],
|
||||
loop: false,
|
||||
onend: this.playNext.bind(this),
|
||||
volume: 0,
|
||||
}),
|
||||
new Howl({
|
||||
src: [war],
|
||||
src: [assetUrl("sounds/music/war.mp3")],
|
||||
loop: false,
|
||||
onend: this.playNext.bind(this),
|
||||
volume: 0,
|
||||
|
||||
@@ -233,20 +233,44 @@ export function getResourcesDir(rootDir: string = process.cwd()): string {
|
||||
return path.join(rootDir, "resources");
|
||||
}
|
||||
|
||||
export function getProprietaryDir(rootDir: string = process.cwd()): string {
|
||||
return path.join(rootDir, "proprietary");
|
||||
}
|
||||
|
||||
// Scans directories with synchronous fs.existsSync — assumes a small number of sourceDirs.
|
||||
function resolveSourceDir(relativePath: string, sourceDirs: string[]): string {
|
||||
for (const dir of sourceDirs) {
|
||||
const candidate = path.join(dir, relativePath);
|
||||
if (fs.existsSync(candidate)) {
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
`Asset ${relativePath} not found in any source directory: ${sourceDirs.join(", ")}`,
|
||||
);
|
||||
}
|
||||
|
||||
function resolveSourceFile(relativePath: string, sourceDirs: string[]): string {
|
||||
return path.join(resolveSourceDir(relativePath, sourceDirs), relativePath);
|
||||
}
|
||||
|
||||
export function shouldKeepRootPublicFile(relativePath: string): boolean {
|
||||
return ROOT_PUBLIC_FILES.has(normalizeAssetPath(relativePath));
|
||||
}
|
||||
|
||||
export function listHashedPublicAssetPaths(resourcesDir: string): string[] {
|
||||
export function listHashedPublicAssetPaths(sourceDirs: string[]): string[] {
|
||||
const files = new Set<string>();
|
||||
for (const pattern of HASHED_PUBLIC_ASSET_GLOBS) {
|
||||
for (const file of globSync(pattern, {
|
||||
cwd: resourcesDir,
|
||||
nodir: true,
|
||||
dot: false,
|
||||
posix: true,
|
||||
})) {
|
||||
files.add(normalizeAssetPath(file));
|
||||
for (const dir of sourceDirs) {
|
||||
if (!fs.existsSync(dir)) continue;
|
||||
for (const pattern of HASHED_PUBLIC_ASSET_GLOBS) {
|
||||
for (const file of globSync(pattern, {
|
||||
cwd: dir,
|
||||
nodir: true,
|
||||
dot: false,
|
||||
posix: true,
|
||||
})) {
|
||||
files.add(normalizeAssetPath(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
return [...files].sort();
|
||||
@@ -264,13 +288,14 @@ export function listRootPublicFiles(resourcesDir: string): string[] {
|
||||
.sort();
|
||||
}
|
||||
|
||||
export function buildPublicAssetManifest(resourcesDir: string): AssetManifest {
|
||||
const cached = manifestCache.get(resourcesDir);
|
||||
export function buildPublicAssetManifest(sourceDirs: string[]): AssetManifest {
|
||||
const cacheKey = sourceDirs.join("\0");
|
||||
const cached = manifestCache.get(cacheKey);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const hashedPublicAssetPaths = listHashedPublicAssetPaths(resourcesDir);
|
||||
const hashedPublicAssetPaths = listHashedPublicAssetPaths(sourceDirs);
|
||||
const rawAssetPaths = hashedPublicAssetPaths.filter(
|
||||
(relativePath) => !isDerivedPublicAsset(relativePath),
|
||||
);
|
||||
@@ -280,14 +305,14 @@ export function buildPublicAssetManifest(resourcesDir: string): AssetManifest {
|
||||
|
||||
const manifest: AssetManifest = {};
|
||||
for (const relativePath of rawAssetPaths) {
|
||||
const absolutePath = path.join(resourcesDir, relativePath);
|
||||
const absolutePath = resolveSourceFile(relativePath, sourceDirs);
|
||||
const hash = createContentHash(absolutePath);
|
||||
manifest[relativePath] = createHashedAssetUrl(relativePath, hash);
|
||||
}
|
||||
|
||||
for (const relativePath of derivedAssetPaths) {
|
||||
const renderedAsset = renderDerivedPublicAsset(
|
||||
resourcesDir,
|
||||
resolveSourceDir(relativePath, sourceDirs),
|
||||
relativePath,
|
||||
manifest,
|
||||
);
|
||||
@@ -301,7 +326,7 @@ export function buildPublicAssetManifest(resourcesDir: string): AssetManifest {
|
||||
);
|
||||
}
|
||||
|
||||
manifestCache.set(resourcesDir, manifest);
|
||||
manifestCache.set(cacheKey, manifest);
|
||||
return manifest;
|
||||
}
|
||||
|
||||
@@ -310,17 +335,18 @@ export function clearPublicAssetManifestCache(): void {
|
||||
}
|
||||
|
||||
export function createHashedPublicAssetFiles(
|
||||
resourcesDir: string,
|
||||
sourceDirs: string[],
|
||||
outDir: string,
|
||||
assetManifest: AssetManifest,
|
||||
): void {
|
||||
for (const [relativePath, hashedUrl] of Object.entries(assetManifest)) {
|
||||
const sourcePath = path.join(resourcesDir, relativePath);
|
||||
const sourceDir = resolveSourceDir(relativePath, sourceDirs);
|
||||
const sourcePath = path.join(sourceDir, relativePath);
|
||||
const outputPath = path.join(outDir, normalizeAssetPath(hashedUrl));
|
||||
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
||||
|
||||
const renderedAsset = renderDerivedPublicAsset(
|
||||
resourcesDir,
|
||||
sourceDir,
|
||||
relativePath,
|
||||
assetManifest,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user