fastdl
This commit is contained in:
parent
31a2ac82d0
commit
bcf798a5f9
4
.env
4
.env
@ -30,6 +30,10 @@ SRCDS_MAPCYCLE="mapcycle.txt"
|
|||||||
|
|
||||||
SRCDS_ADMINS="STEAM_1:1:12270329"
|
SRCDS_ADMINS="STEAM_1:1:12270329"
|
||||||
|
|
||||||
|
# ----------
|
||||||
|
# fastdl
|
||||||
|
FASTDL_URL=https://fastdl.megastructure.surf
|
||||||
|
|
||||||
# ----------
|
# ----------
|
||||||
# mariadb
|
# mariadb
|
||||||
MARIADB_ROOT_PASSWORD="surfsup"
|
MARIADB_ROOT_PASSWORD="surfsup"
|
||||||
|
|||||||
@ -24,6 +24,7 @@ services:
|
|||||||
- ./etc:/home/steam/etc
|
- ./etc:/home/steam/etc
|
||||||
- ./data/cssds:/home/steam/cssds
|
- ./data/cssds:/home/steam/cssds
|
||||||
- tf2ds:/home/steam/tf2ds
|
- tf2ds:/home/steam/tf2ds
|
||||||
|
- fastdl-maps:/fastdl/maps:ro
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: mariadb:12
|
image: mariadb:12
|
||||||
@ -47,8 +48,8 @@ services:
|
|||||||
# Mount credentials (read-only)
|
# Mount credentials (read-only)
|
||||||
- ./src/mapsdl/credentials:/app/credentials:ro
|
- ./src/mapsdl/credentials:/app/credentials:ro
|
||||||
|
|
||||||
# Mount maps directory (read-write)
|
# Output bz2 files to shared FastDL volume
|
||||||
- ./data/cssds/cstrike/maps:/maps
|
- fastdl-maps:/maps
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
- MAPS_DIR=/maps
|
- MAPS_DIR=/maps
|
||||||
@ -62,5 +63,13 @@ services:
|
|||||||
# Run once and exit
|
# Run once and exit
|
||||||
restart: "no"
|
restart: "no"
|
||||||
|
|
||||||
|
fastdl:
|
||||||
|
build: ./src/fastdl
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
volumes:
|
||||||
|
- fastdl-maps:/srv/maps:ro
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
tf2ds:
|
tf2ds:
|
||||||
|
fastdl-maps:
|
||||||
|
|||||||
1
etc/cfg/mapcycle.txt
Normal file
1
etc/cfg/mapcycle.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
surf_boreas
|
||||||
51
etc/run.sh
51
etc/run.sh
@ -451,6 +451,55 @@ EOF
|
|||||||
cfg() {
|
cfg() {
|
||||||
cd
|
cd
|
||||||
cp etc/cfg/server.cfg cssds/cstrike/cfg/
|
cp etc/cfg/server.cfg cssds/cstrike/cfg/
|
||||||
|
cp etc/cfg/mapcycle.txt cssds/cstrike/cfg/
|
||||||
|
sed -i "s|sv_downloadurl .*|sv_downloadurl \"${FASTDL_URL:-}/cstrike/\"|" "$CSTRIKE/cfg/server.cfg"
|
||||||
|
}
|
||||||
|
|
||||||
|
populate_maps() {
|
||||||
|
local BZ2_DIR="/fastdl/maps"
|
||||||
|
local MAPS_DIR="$CSTRIKE/maps"
|
||||||
|
local MAPCYCLE="$CSTRIKE/cfg/mapcycle.txt"
|
||||||
|
|
||||||
|
echo "--------------------------------------------------------------"
|
||||||
|
echo "Populating maps from compressed archive"
|
||||||
|
echo "--------------------------------------------------------------"
|
||||||
|
|
||||||
|
if [ ! -f "$MAPCYCLE" ]; then
|
||||||
|
echo "No mapcycle.txt found, skipping map population"
|
||||||
|
echo "--------------------------------------------------------------"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$MAPS_DIR"
|
||||||
|
|
||||||
|
local total=0 skipped=0 extracted=0 missing=0
|
||||||
|
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
# skip empty lines and comments
|
||||||
|
line=$(echo "$line" | sed 's|//.*||' | xargs)
|
||||||
|
[ -z "$line" ] && continue
|
||||||
|
|
||||||
|
total=$((total + 1))
|
||||||
|
local bz2="$BZ2_DIR/${line}.bsp.bz2"
|
||||||
|
local bsp="$MAPS_DIR/${line}.bsp"
|
||||||
|
|
||||||
|
if [ -f "$bsp" ]; then
|
||||||
|
skipped=$((skipped + 1))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$bz2" ]; then
|
||||||
|
missing=$((missing + 1))
|
||||||
|
echo " [!] ${line}: not in fastdl volume"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
bunzip2 -c "$bz2" > "$bsp"
|
||||||
|
extracted=$((extracted + 1))
|
||||||
|
done < "$MAPCYCLE"
|
||||||
|
|
||||||
|
echo "Maps: $total in mapcycle, $extracted extracted, $skipped already present, $missing not available"
|
||||||
|
echo "--------------------------------------------------------------"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -482,6 +531,8 @@ main() {
|
|||||||
# Zones will be available after server starts and creates tables
|
# Zones will be available after server starts and creates tables
|
||||||
import_zone_data
|
import_zone_data
|
||||||
|
|
||||||
|
populate_maps
|
||||||
|
|
||||||
run_cssds
|
run_cssds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
src/fastdl/Dockerfile
Normal file
2
src/fastdl/Dockerfile
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
FROM nginx:alpine
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
8
src/fastdl/nginx.conf
Normal file
8
src/fastdl/nginx.conf
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
root /srv;
|
||||||
|
location /cstrike/maps/ {
|
||||||
|
alias /srv/maps/;
|
||||||
|
autoindex off;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -156,8 +156,8 @@ export async function getLocalMaps(mapsDir: string): Promise<Set<string>> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
for await (const entry of Deno.readDir(mapsDir)) {
|
for await (const entry of Deno.readDir(mapsDir)) {
|
||||||
if (entry.isFile && entry.name.endsWith(".bsp")) {
|
if (entry.isFile && entry.name.endsWith(".bsp.bz2")) {
|
||||||
const mapName = entry.name.replace(/\.bsp$/, "");
|
const mapName = entry.name.replace(/\.bsp\.bz2$/, "");
|
||||||
localMaps.add(mapName);
|
localMaps.add(mapName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -317,29 +317,32 @@ export async function downloadAndExtractMaps(
|
|||||||
console.log(`[${completed}/${mapsToDownload.size}] Downloading ${mapName}...`);
|
console.log(`[${completed}/${mapsToDownload.size}] Downloading ${mapName}...`);
|
||||||
|
|
||||||
const archivePath = join(config.tempDir, gdriveFile.name);
|
const archivePath = join(config.tempDir, gdriveFile.name);
|
||||||
const bspPath = join(config.mapsDir, `${mapName}.bsp`);
|
const tempBspPath = join(config.tempDir, `${mapName}.bsp`);
|
||||||
|
const bz2Path = join(config.mapsDir, `${mapName}.bsp.bz2`);
|
||||||
|
|
||||||
await client.downloadFile(gdriveFile.id, archivePath);
|
await client.downloadFile(gdriveFile.id, archivePath);
|
||||||
|
|
||||||
if (gdriveFile.name.endsWith(".rar")) {
|
if (gdriveFile.name.endsWith(".rar")) {
|
||||||
await extractRAR(archivePath, mapName, config.mapsDir, config.tempDir);
|
await extractRAR(archivePath, mapName, config.tempDir, config.tempDir);
|
||||||
await Deno.remove(archivePath);
|
await Deno.remove(archivePath);
|
||||||
} else if (gdriveFile.name.endsWith(".bz2")) {
|
} else if (gdriveFile.name.endsWith(".bz2")) {
|
||||||
await decompressBZ2(archivePath, bspPath);
|
await decompressBZ2(archivePath, tempBspPath);
|
||||||
await Deno.remove(archivePath);
|
await Deno.remove(archivePath);
|
||||||
} else if (gdriveFile.name.endsWith(".zip")) {
|
} else if (gdriveFile.name.endsWith(".zip")) {
|
||||||
await extractZIP(archivePath, mapName, config.mapsDir, config.tempDir);
|
await extractZIP(archivePath, mapName, config.tempDir, config.tempDir);
|
||||||
await Deno.remove(archivePath);
|
await Deno.remove(archivePath);
|
||||||
} else if (gdriveFile.name.endsWith(".bsp")) {
|
} else if (gdriveFile.name.endsWith(".bsp")) {
|
||||||
await moveFile(archivePath, bspPath);
|
await moveFile(archivePath, tempBspPath);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Unknown archive format: ${gdriveFile.name}`);
|
throw new Error(`Unknown archive format: ${gdriveFile.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await verifyBSP(bspPath);
|
await verifyBSP(tempBspPath);
|
||||||
|
await compressBSP(tempBspPath, bz2Path);
|
||||||
|
await Deno.remove(tempBspPath);
|
||||||
|
|
||||||
stats.success++;
|
stats.success++;
|
||||||
console.log(` [OK] ${mapName} downloaded and extracted`);
|
console.log(` [OK] ${mapName} downloaded and compressed`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
stats.failed++;
|
stats.failed++;
|
||||||
console.error(` [X] Failed to download ${mapName}: ${(error as Error).message}`);
|
console.error(` [X] Failed to download ${mapName}: ${(error as Error).message}`);
|
||||||
@ -459,3 +462,13 @@ export async function verifyBSP(bspPath: string): Promise<void> {
|
|||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function compressBSP(bspPath: string, bz2Path: string): Promise<void> {
|
||||||
|
const process = new Deno.Command("bzip2", {
|
||||||
|
args: ["-c", bspPath],
|
||||||
|
stdout: "piped",
|
||||||
|
});
|
||||||
|
const { code, stdout } = await process.output();
|
||||||
|
if (code !== 0) throw new Error(`bzip2 failed with exit code ${code}`);
|
||||||
|
await Deno.writeFile(bz2Path, stdout);
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user