Cobblemon Survival Pack: phases 0-2 and Phase 3 zones and hazards
Free NeoForge 1.21.1 modpack on Cobblemon 1.8.4 built with packwiz, plus the Kotlin companion mod (cobblesurvival) and its built-in data pack. - Phase 0: packwiz pack with Modrinth/CurseForge cross-links, build and export tooling, Prism instance sync, test server with smoke checks. - Phase 1: Poke Ball shaped island (WorldGen Editor preset, seed isle-26), Adrian's biome list, no natural ore, structures without ore or vanilla mobs, no Nether portals, world template builder and world report. - Phase 2: permadeath (CobbleHardcoreMon plus the companion mod's gap fixes), Revival Charm, expensive Healing Machine, Lootr refreshing loot, in-game self-tests on Minecraft's GameTest framework. - Phase 3 so far: 14 zones with their own spawn pools and level bands (tools/zone_layout.py, tools/zone_spawns.py, "zone" spawn condition), Cold Sweat heat and cold as gates with boss-drop insulation recipes, spawn survey and hazard self-tests in tools/quick_check.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
398301f0be
232 files changed
+72665
No files matched your search
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Static dependency check for a folder of NeoForge mod jars.
|
||||
|
||||
Reads META-INF/neoforge.mods.toml from every jar (and from jar-in-jar files
|
||||
under META-INF/jarjar/), then checks that each required dependency is present
|
||||
and inside its declared version range. Needs no server and no EULA, so it runs
|
||||
anywhere; the server smoke test is still the real proof.
|
||||
|
||||
Version ranges use Maven syntax ("[1.8.4,1.9)", "[21.1,)", "1.0" = at least).
|
||||
Build metadata after "+" is ignored, so Cobblemon "1.8.4+1.21.1" compares as
|
||||
1.8.4; qualifiers follow Maven (21-beta < 21 < 21.1.265). A mismatch on the `minecraft` dependency is only a warning: every JEI
|
||||
build for 1.21.1 declares "[1.21, 1.21.1)" and NeoForge 21.1 still loads it
|
||||
(checked on the test server, 2026-10-28), so this check is stricter than the
|
||||
loader there.
|
||||
|
||||
Usage: tools/check_deps.py <mods_dir> [--neoforge VER] [--minecraft VER]
|
||||
"""
|
||||
import argparse
|
||||
import io
|
||||
import re
|
||||
import sys
|
||||
import tomllib
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
PLATFORM = {"neoforge", "minecraft", "javafml", "fml", "kotlinforforge_loader"}
|
||||
|
||||
|
||||
QUALIFIERS = {"alpha": 1, "a": 1, "beta": 2, "b": 2, "milestone": 3, "m": 3, "rc": 4, "cr": 4, "snapshot": 5}
|
||||
RELEASE = (0, 10, "") # pads the shorter version: a release sorts above its own alpha/beta/rc
|
||||
|
||||
|
||||
def key(version):
|
||||
"""Maven-like ordering: numbers sort above qualifiers, so 21.1.265 > 21-beta > 21-alpha."""
|
||||
parts = re.split(r"[.\-]", version.split("+", 1)[0])
|
||||
return [(1, int(p), "") if p.isdigit() else (0, QUALIFIERS.get(p.lower(), 6), p.lower()) for p in parts if p]
|
||||
|
||||
|
||||
def compare(a, b):
|
||||
ka, kb = key(a), key(b)
|
||||
n = max(len(ka), len(kb))
|
||||
ka, kb = ka + [RELEASE] * (n - len(ka)), kb + [RELEASE] * (n - len(kb))
|
||||
return (ka > kb) - (ka < kb)
|
||||
|
||||
|
||||
def in_range(version, spec):
|
||||
spec = spec.strip()
|
||||
if spec in ("", "*"):
|
||||
return True
|
||||
if spec[0] not in "[(":
|
||||
return compare(version, spec) >= 0
|
||||
for part in re.findall(r"[\[(][^\])]*[\])]", spec):
|
||||
lo_inc, hi_inc = part[0] == "[", part[-1] == "]"
|
||||
bounds = part[1:-1].split(",")
|
||||
if len(bounds) == 1:
|
||||
if compare(version, bounds[0]) == 0:
|
||||
return True
|
||||
continue
|
||||
lo, hi = (b.strip() for b in bounds)
|
||||
if lo and (compare(version, lo) < 0 or (compare(version, lo) == 0 and not lo_inc)):
|
||||
continue
|
||||
if hi and (compare(version, hi) > 0 or (compare(version, hi) == 0 and not hi_inc)):
|
||||
continue
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def read_mods(data, name, found, deps):
|
||||
z = zipfile.ZipFile(io.BytesIO(data))
|
||||
names = set(z.namelist())
|
||||
if "META-INF/neoforge.mods.toml" in names:
|
||||
toml = tomllib.loads(z.read("META-INF/neoforge.mods.toml").decode("utf-8", "replace"))
|
||||
manifest = z.read("META-INF/MANIFEST.MF").decode("utf-8", "replace") if "META-INF/MANIFEST.MF" in names else ""
|
||||
jar_version = (re.search(r"Implementation-Version: *(\S+)", manifest) or [None, "0"])[1]
|
||||
for mod in toml.get("mods", []):
|
||||
version = str(mod.get("version", "0")).replace("${file.jarVersion}", jar_version)
|
||||
found[mod["modId"]] = (version, name)
|
||||
for mod_id, entries in toml.get("dependencies", {}).items():
|
||||
for dep in entries:
|
||||
required = dep.get("type", "required" if dep.get("mandatory", True) else "optional") == "required"
|
||||
if required:
|
||||
deps.append((mod_id, dep["modId"], dep.get("versionRange", "*"), name))
|
||||
for inner in (n for n in names if n.startswith("META-INF/jarjar/") and n.endswith(".jar")):
|
||||
read_mods(z.read(inner), f"{name}!{inner.rsplit('/', 1)[-1]}", found, deps)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.add_argument("mods_dir", type=Path)
|
||||
parser.add_argument("--neoforge")
|
||||
parser.add_argument("--minecraft", default="1.21.1")
|
||||
args = parser.parse_args()
|
||||
|
||||
found, deps = {}, []
|
||||
for jar in sorted(args.mods_dir.glob("*.jar")):
|
||||
read_mods(jar.read_bytes(), jar.name, found, deps)
|
||||
if args.neoforge:
|
||||
found["neoforge"] = (args.neoforge, "loader")
|
||||
found["minecraft"] = (args.minecraft, "game")
|
||||
|
||||
problems, warnings = [], []
|
||||
for owner, dep, spec, jar in deps:
|
||||
if dep not in found:
|
||||
if dep not in PLATFORM:
|
||||
problems.append(f"{owner} ({jar}) needs {dep} {spec}: not installed")
|
||||
elif not in_range(found[dep][0], spec):
|
||||
(warnings if dep == "minecraft" else problems).append(
|
||||
f"{owner} ({jar}) needs {dep} {spec}: found {found[dep][0]}")
|
||||
print(f"{len(found)} mods, {len(deps)} required dependencies checked")
|
||||
for w in warnings:
|
||||
print(f" warning: {w}")
|
||||
if problems:
|
||||
print("\n".join(f" - {p}" for p in problems))
|
||||
sys.exit(1)
|
||||
print("all required dependencies satisfied")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user