server update

This commit is contained in:
Sashegdev
2026-04-04 14:57:15 +00:00
parent cf4a5c74e5
commit 7670edbff7
10 changed files with 1132 additions and 0 deletions
+178
View File
@@ -0,0 +1,178 @@
# pack_manager.py (updated)
import hashlib
import os
from datetime import datetime
from pathlib import Path
import json
import aiofiles
from typing import Optional, Dict
import structlog
from models import PackMeta, FileEntry
import aiohttp
from typing import List, Optional
logger = structlog.get_logger(__name__)
PACKS_DIR = Path("packs")
DATA_DIR = Path("data")
DATA_DIR.mkdir(exist_ok=True)
MINECRAFT_VERSION_MANIFEST_URL = "https://launchermeta.mojang.com/mc/game/version_manifest.json"
FABRIC_META_URL = "https://meta.fabricmc.net/v2/versions"
FORGE_META_URL = "https://files.minecraftforge.net/net/minecraftforge/forge/"
MinecraftVersion=[]
# Cache for loaded manifests
_manifest_cache: Dict[str, PackMeta] = {}
def get_cached_manifest(pack_name: str) -> Optional[PackMeta]:
"""Get manifest from memory cache if available"""
return _manifest_cache.get(pack_name)
def get_meta_path(pack_name: str) -> Path:
return DATA_DIR / f"{pack_name}.meta"
async def calculate_sha256(file_path: Path) -> str:
hash_sha = hashlib.sha256()
async with aiofiles.open(file_path, 'rb') as f:
while chunk := await f.read(8192):
hash_sha.update(chunk)
return hash_sha.hexdigest()
async def get_minecraft_versions() -> List[MinecraftVersion]:
"""Fetch available Minecraft versions from Mojang"""
async with aiohttp.ClientSession() as session:
async with session.get(MINECRAFT_VERSION_MANIFEST_URL) as response:
data = await response.json()
versions = []
for v in data.get("versions", []):
versions.append(MinecraftVersion(
version=v["id"],
type=v["type"],
release_time=datetime.fromisoformat(v["releaseTime"].replace('Z', '+00:00')),
url=v["url"]
))
return versions
async def get_fabric_versions(minecraft_version: str) -> List[str]:
"""Get available Fabric versions for specific Minecraft version"""
async with aiohttp.ClientSession() as session:
async with session.get(f"{FABRIC_META_URL}/loader") as response:
data = await response.json()
fabric_versions = []
for loader in data:
if loader.get("stable", True):
fabric_versions.append(loader["version"])
return fabric_versions
async def get_forge_versions(minecraft_version: str) -> List[str]:
"""Get available Forge versions for specific Minecraft version"""
async with aiohttp.ClientSession() as session:
async with session.get(FORGE_META_URL) as response:
# Forge API is more complex, simplified for now
# You might want to use a proper forge API
return [] # Placeholder
async def download_minecraft_version(version: str, target_path: Path) -> bool:
"""Download Minecraft version JSON and client jar"""
# Get version manifest
async with aiohttp.ClientSession() as session:
async with session.get(f"https://piston-meta.mojang.com/mc/game/{version}/{version}.json") as response:
if response.status == 200:
version_data = await response.json()
# Save version JSON
async with aiofiles.open(target_path / f"{version}.json", 'w') as f:
await f.write(json.dumps(version_data, indent=2))
# Download client jar
downloads = version_data.get("downloads", {})
client_info = downloads.get("client", {})
if client_info:
client_url = client_info.get("url")
async with session.get(client_url) as client_response:
if client_response.status == 200:
async with aiofiles.open(target_path / f"{version}.jar", 'wb') as f:
async for chunk in client_response.content.iter_chunked(8192):
await f.write(chunk)
return True
return False
async def scan_pack(pack_name: str, force_rescan: bool = False) -> PackMeta:
"""Scan pack directory and update manifest if needed"""
pack_path = PACKS_DIR / pack_name
if not pack_path.exists() or not pack_path.is_dir():
raise FileNotFoundError(f"Pack {pack_name} not found")
meta_path = get_meta_path(pack_name)
current_meta: Optional[PackMeta] = None
# Check if we have cached version and force_rescan is False
if not force_rescan and pack_name in _manifest_cache:
return _manifest_cache[pack_name]
if meta_path.exists():
async with aiofiles.open(meta_path, 'r', encoding='utf-8') as f:
data = json.loads(await f.read())
current_meta = PackMeta.model_validate(data)
new_files: Dict[str, FileEntry] = {}
changed = False
for root, dirs, files in os.walk(pack_path):
# Filter ignored directories
ignored = current_meta.ignored_dirs if current_meta else []
dirs[:] = [d for d in dirs if d not in ignored]
for file in files:
file_path = Path(root) / file
rel_path = file_path.relative_to(pack_path).as_posix()
# Skip files in ignored directories
if any(ignored_dir in rel_path.split('/') for ignored_dir in ignored):
continue
stat = file_path.stat()
file_hash = await calculate_sha256(file_path)
entry = FileEntry(
path=rel_path,
hash=file_hash,
size=stat.st_size,
added_at=datetime.utcfromtimestamp(stat.st_ctime),
modified_at=datetime.utcfromtimestamp(stat.st_mtime)
)
new_files[rel_path] = entry
if current_meta and (rel_path not in current_meta.files or
current_meta.files[rel_path].hash != file_hash):
changed = True
# Update manifest if changes detected or new pack
if not current_meta or changed or len(new_files) != len(current_meta.files if current_meta else 0):
version = (current_meta.version + 1) if current_meta else 1
new_meta = PackMeta(
pack_name=pack_name,
version=version,
files=new_files,
updated_at=datetime.utcnow(),
ignored_dirs=current_meta.ignored_dirs if current_meta else []
)
async with aiofiles.open(meta_path, 'w', encoding='utf-8') as f:
await f.write(new_meta.model_dump_json(indent=2))
# Update memory cache
_manifest_cache[pack_name] = new_meta
logger.info("Pack updated", pack=pack_name, new_version=version, files_count=len(new_files))
return new_meta
# Update cache with existing manifest
if current_meta:
_manifest_cache[pack_name] = current_meta
return current_meta