This is an experimental copy for testing Poikilos' issue mirroring system. Note that Gitea's migration tool can import issues, but the "Issues" checkbox is disabled when "This repository will be a mirror" is enabled (it is for this repo).
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

437 lines
20 KiB

#!/usr/bin/env python3
'''
ENLIVEN subgame builder - creates ENLIVEN based on minetest_game
Merges mods & settings from old bash installer script
'''
from __future__ import print_function
import sys
import os
import argparse
# import configparser
import shutil
from typing import List, Dict, Union
# Assuming these exist in your pyenliven module
from pyenliven import (
echo0,
# getSGPath, # not used here anymore
# profile,
MODS_STOPGAP_DIR,
)
# ──────────────────────────────────────────────────────────────
# M O D L I S T F R O M B A S H S C R I P T
# ──────────────────────────────────────────────────────────────
# Format:
# 'name': folder name expected in mods/ or mods_stopgap/
# 'repo': git URL (str or list[str] — first = highest priority)
# 'branch': optional branch name
# 'stopgap_only': True → only use from MODS_STOPGAP_DIR, ignore repo
gamespec: Dict[str, any] = {}
gamespec['remove_mods'] = [
"facade", # no recipes
"placecraft", # interferes with eating
"more_chests", # https://github.com/poikilos/EnlivenMinetest/issues/446
"emeralds", # https://github.com/poikilos/EnlivenMinetest/issues/497
"give_initial_stuff",
"xban2",
"dynamic_liquid",
"stamina",
"hbarmor",
"hbhunger",
"hudbars",
"hbsprint",
"dungeon_loot", # replaced by treasurer + trm_*
"helicopter", # known crash issues in older versions
# add others from issue #310 if desired
]
gamespec['add_mods']: List[Dict[str, any]] = [
# ── Utility / Admin ────────────────────────────────────────
{'name': 'invhack', 'repo': "https://github.com/salahzar/minetest-invhack.git"},
{'name': 'worldedit', 'repo': "https://github.com/Uberi/MineTest-WorldEdit.git"},
{'name': 'metatools', 'repo': "https://github.com/poikilos/metatools.git"},
{'name': 'protector', 'repo': "https://notabug.org/TenPlus1/protector.git"},
{'name': 'advancedban', 'repo': "https://github.com/srifqi/advancedban"},
{'name': 'areas', 'repo': "https://github.com/ShadowNinja/areas.git"},
{'name': 'whitelist', 'repo': "https://github.com/ShadowNinja/whitelist.git"},
{'name': 'vote', 'repo': "https://github.com/minetest-mods/vote.git"},
# ── Mobs & Worldgen ────────────────────────────────────────
{'name': 'worldedge', 'repo': "https://github.com/minetest-mods/worldedge.git"},
{'name': 'mobs_redo', 'repo': "https://notabug.org/TenPlus1/mobs_redo.git"},
{'name': 'mobs_monster', 'repo': "https://notabug.org/TenPlus1/mobs_monster.git"},
{'name': 'mobs_animal', 'repo': "https://notabug.org/TenPlus1/mobs_animal.git"},
{'name': 'mob_horse', 'repo': "https://notabug.org/tenplus1/mob_horse.git"},
{'name': 'mobs_sky', 'repo': "https://github.com/poikilos/mobs_sky.git"},
{'name': 'spawners', 'repo': "https://bitbucket.org/minetest_gamers/spawners.git"},
{'name': 'tsm_pyramids', 'repo': "https://github.com/poikilos/tsm_pyramids.git"},
{'name': 'tsm_chests_dungeon', 'repo': "http://repo.or.cz/minetest_tsm_chests_dungeon.git"},
{'name': 'treasurer', 'repo': "http://repo.or.cz/minetest_treasurer.git"},
{'name': 'trm_pyramids'}, # special – files copied directly in bash → handle manually or stopgap
{'name': 'moreblocks', 'repo': "https://github.com/minetest-mods/moreblocks.git"},
{'name': 'plantlife_modpack','repo': "https://github.com/mt-mods/plantlife_modpack.git"},
{'name': 'bushes_soil', 'repo': "https://github.com/poikilos/bushes_soil.git"},
{'name': 'lapis', 'repo': "https://github.com/Napiophelios/LapisLazuli.git"},
{'name': 'biome_lib', 'repo': "https://github.com/mt-mods/biome_lib.git"},
{'name': 'moretrees', 'repo': "https://github.com/mt-mods/moretrees.git"},
{'name': 'mesecons', 'repo': "https://github.com/minetest-mods/mesecons"},
{'name': 'pipeworks', 'repo': "https://github.com/mt-mods/pipeworks.git"},
{'name': 'technic', 'repo': "https://github.com/minetest-mods/technic.git"},
{'name': 'technic_armor', 'repo': "https://github.com/stujones11/technic_armor.git"},
{'name': 'mapgen_helper', 'repo': "https://github.com/minetest-mods/mapgen_helper.git"},
{'name': 'subterrane', 'repo': "https://github.com/minetest-mods/subterrane.git"},
{'name': 'caverealms', 'repo': "https://github.com/FaceDeer/minetest-caverealms.git"},
{'name': 'moreores', 'repo': "https://github.com/minetest-mods/moreores.git"},
{'name': 'tsm_mines', 'repo': "http://repo.or.cz/tsm_mines.git"},
{'name': 'tsm_railcorridors','repo': "http://repo.or.cz/RailCorridors/tsm_railcorridors.git"},
{'name': 'birthstones', 'repo': "https://github.com/poikilos/birthstones.git"},
{'name': 'bakedclay', 'repo': "https://notabug.org/tenplus1/bakedclay.git"},
{'name': 'quartz', 'repo': "https://github.com/minetest-mods/quartz"},
{'name': 'magma_conduits', 'repo': "https://github.com/FaceDeer/magma_conduits.git"},
{'name': 'boost_cart', 'repo': "https://github.com/SmallJoker/boost_cart.git"},
# ── Gameplay / Items ───────────────────────────────────────
{'name': 'throwing', 'repo': "https://github.com/minetest-mods/throwing.git"},
{'name': 'throwing_arrows', 'repo': "https://github.com/minetest-mods/throwing_arrows.git"},
{'name': 'fishing', 'repo': "https://github.com/MinetestForFun/fishing.git",
'issues': ["Make sure fishing rods recipe works"]},
{'name': 'compassgps', 'repo': "https://github.com/poikilos/compassgps.git"},
{'name': 'sounding_line', 'repo': "https://github.com/minetest-mods/sounding_line.git"},
{'name': 'mywalls', 'repo': "https://github.com/minetest-mods/mywalls.git"},
{'name': 'mymasonhammer', 'repo': "https://github.com/minetest-mods/mymasonhammer.git"},
{'name': 'ts_furniture', 'repo': "https://github.com/minetest-mods/ts_furniture.git"},
{'name': '3d_armor', 'repo': "https://github.com/stujones11/minetest-3d_armor.git"},
{'name': 'basic_materials','repo': "https://github.com/mt-mods/basic_materials.git"},
{'name': 'homedecor_modpack','repo': "https://github.com/mt-mods/homedecor_modpack.git"},
{'name': 'homedecor_ua', 'repo': "https://github.com/poikilos/homedecor_ua.git"},
{'name': 'unifieddyes', 'repo': "https://github.com/mt-mods/unifieddyes.git"},
{'name': 'travelnet', 'repo': "https://github.com/Sokomine/travelnet.git"},
{'name': 'anvil', 'repo': "https://github.com/minetest-mods/anvil.git"},
{'name': 'sling', 'repo': "https://github.com/minetest-mods/sling.git"},
{'name': 'signs_lib', 'repo': "https://github.com/mt-mods/signs_lib.git"},
{'name': 'slimenodes', 'repo': "https://github.com/poikilos/slimenodes.git"},
{'name': 'ropes', 'repo': "https://github.com/minetest-mods/ropes.git"},
{'name': 'digilines', 'repo': "https://github.com/minetest-mods/digilines.git"},
{'name': 'trmp_minetest_game','repo': "https://github.com/poikilos/trmp_minetest_game.git"},
{'name': 'awards', 'repo': "https://gitlab.com/rubenwardy/awards.git"},
{'name': 'awards_board', 'repo': "https://framagit.org/xisd-minetest/awards_board.git"},
{'name': 'item_drop', 'repo': "https://github.com/minetest-mods/item_drop.git"},
{'name': 'sponge', 'repo': "https://github.com/BenjieFiftysix/sponge"},
# ── Player UX ──────────────────────────────────────────────
{'name': 'money', 'repo': "https://notabug.org/TenPlus1/money"},
{'name': 'lightning', 'repo': "https://github.com/minetest-mods/lightning.git"},
{'name': 'unified_inventory','repo': [
"https://github.com/minetest-mods/unified_inventory.git", # official
"https://github.com/MinetestForFun/unified_inventory" # old bash preference
]},
{'name': 'player_monoids', 'repo': "https://github.com/minetest-mods/player_monoids.git"},
{'name': 'sprint', 'repo': "https://github.com/GunshipPenguin/sprint.git"},
{'name': 'hunger_ng', 'repo': "https://gitlab.com/4w/hunger_ng.git"},
{'name': 'playereffects', 'repo': "https://github.com/sys4-fr/playereffects"},
{'name': 'ambience', 'repo': "https://notabug.org/tenplus1/ambience.git"},
{'name': 'playeranim', 'repo': "https://github.com/minetest-mods/playeranim.git"},
{'name': 'skinsdb', 'repo': "https://github.com/minetest-mods/skinsdb.git"},
{'name': 'woodcutting', 'repo': "https://github.com/minetest-mods/woodcutting.git"},
# ── Legacy / Special ───────────────────────────────────────
#{'name': 'animal_materials_legacy'},
#{'name': 'elk_legacy'},
#{'name': 'glooptest_missing'},
#{'name': 'nftools_legacy'},
]
'''
mt_conf_by_mod settings should be placed in minetest.conf such as
/opt/minebest/mtworlds/center/ENLIVEN/minetest.conf
but for now just use
- patches/subgame/minetest.conf
To define the game.
If minebest is present, combine minetest.conf and
minetest.server-example.conf
but maybe make an alternate version with stuff that isn't in
world.conf.
For other conf settings:
- patches/subgame/minetest.server-example.conf goes in the server only.
- Place the result in the game directory such as will result in
/opt/minebest/mtworlds/center/ENLIVEN/minetest.conf
- patches/subgame/minetest.client-example.conf goes in clients only.
'''
BASE_ENLIVEN_CONF_SETTINGS = [
# General / map
"enable_lapis_mod_columns = true",
"map_generation_limit = 5000",
# Protector
"protector_radius = 7",
"protector_flip = true",
"protector_pvp = true",
"protector_pvp_spawn = 10",
"protector_drop = false",
"protector_hurt = 3",
# Other gameplay
"world_edge = 5000",
"default_privs = interact,shout,home",
"max_users = 50",
"motd = \"Actions and chat messages are logged. Use inventory to see recipes (use web for live map if available).\"",
"disallow_empty_passwords = true",
"secure.trusted_mods = advanced_npc",
"server_dedicated = false",
"bones_position_message = true",
# Sprint (GunshipPenguin sprint settings)
"sprint_speed = 2.25",
"sprint_jump = 1.25",
"sprint_stamina_drain = .5",
]
# Per-mod overrides / extras
mt_conf_by_mod = {
'item_drop': {
'item_drop.pickup_radius': "1.425",
},
'throwing_arrows': {
'throwing.enable_arrow': "true",
},
}
why = {}
why["https://github.com/MinetestForFun/unified_inventory"] = '''
This fork makes a "nicer interface". The fork hasn't been tested yet.
'''
# deprecates https://github.com/poikilos/vines.git fork of Facedeer's:
why["https://github.com/FaceDeer/vines.git"] = '''
> I've finally done it, I've split this mod in twain. The new
> stand-alone ropes mod has no dependency on biome_lib and no vine
> content, though its crafting recipes remain compatible with the vines
> produced by this mod.
>
> My fork of this vines mod has had the rope-related content removed
> from it, leaving it as just a vines mod. Note that I haven't tested
> it extensively - I have to admit, I've mainly been in this for the
> ropes. :) I'll do what I can to maintain it, though, if anyone has
> bug reports or requests.
>
> I've added a node upgrade function to the new ropes mod that will
> convert the ropes from both my fork of the vines mod and the original
> version of the vines mod by bas080 to the new ropes mod's ropes. So
> if you wish to upgrade an existing world it should work.
- FaceDeer on [[Mod] Vines and Rope [2.3] [vines]]
(https://forums.minetest.org/viewtopic.php?f=11&t=2344&start=50
&sid=bf15c996963e891cd3f2460c2525044a)
Note that vines requires:
default
biome_lib
moretrees?
doc?
intllib?
mobs?
creatures?
'''
gamespec['disable_mobs'] = [
"old_lady",
]
"""
known_issues = {}
known_issues['fishing'] = "Make sure fishing rods recipe works"
server_only_mods = [
'ircpack',
'chat3', # debatable – was removed in some places
]
# ──────────────────────────────────────────────────────────────
class GameBuilder:
def __init__(self, minetest_game_path: str, minetest_version: str = "5"):
self.source_game = os.path.realpath(minetest_game_path)
self.target_parent = os.path.dirname(self.source_game)
self.target_game = os.path.join(self.target_parent, "ENLIVEN")
self.mods_target = os.path.join(self.target_game, "mods")
self.minetest_version = minetest_version # "5" or "0.4"
if minetest_version not in ("5", "0.4"):
raise ValueError("minetest_version must be '5' or '0.4'")
if not os.path.isdir(self.source_game):
raise FileNotFoundError(f"minetest_game not found: {self.source_game}")
echo0(f"Building ENLIVEN → {self.target_game}")
echo0(f" from base: {self.source_game}")
echo0(f" target Minetest compatibility: {self.minetest_version}")
def prepare_target(self):
"""Copy minetest_game ENLIVEN if needed"""
if os.path.exists(self.target_game):
echo0(f"Target already exists: {self.target_game}")
echo0("→ delete it manually if you want fresh copy")
return
echo0("Copying minetest_game → ENLIVEN ...")
shutil.copytree(self.source_game, self.target_game)
def install_mod(self, entry: Dict[str, any]):
name = entry.get('name')
repo = entry.get('repo')
branch = entry.get('branch')
stopgap_only = entry.get('stopgap_only', False)
if not name:
echo0("Skipping entry without 'name'")
return
dest = os.path.join(self.mods_target, name)
# 1. Prefer stopgap if exists
stopgap_src = os.path.join(MODS_STOPGAP_DIR, name)
if os.path.isdir(stopgap_src):
echo0(f" [stopgap] {name}")
if os.path.exists(dest):
shutil.rmtree(dest)
shutil.copytree(stopgap_src, dest)
return
# 2. Git clone if we have repo URL(s)
if repo and not stopgap_only:
urls = [repo] if isinstance(repo, str) else repo
cloned = False
for url in urls:
echo0(f" trying → {url}")
# Here should be real git clone logic.
# For now just placeholder message.
# You can use subprocess.run(["git", "clone", ...])
echo0(f" [TODO git clone] {url}{dest}")
cloned = True # pretend success
break
if cloned:
return
echo0(f"→ WARNING: no source found for {name}")
def remove_mod(self, modname: str):
path = os.path.join(self.mods_target, modname)
if os.path.isdir(path):
echo0(f" removing {modname}")
shutil.rmtree(path)
def apply_remove_list(self):
for m in gamespec.get('remove_mods', []):
self.remove_mod(m)
def install_all_mods(self):
for entry in gamespec.get('add_mods', []):
self.install_mod(entry)
def write_game_conf(self):
path = os.path.join(self.target_game, "game.conf")
with open(path, "w", encoding="utf-8") as f:
f.write("name = ENLIVEN\n")
f.write("description = Enhanced minetest_game experience\n")
def update_conf(self, path: str):
"""Append settings only if not already present (line-based, stripped comparison)"""
os.makedirs(os.path.dirname(path), exist_ok=True)
# Collect base settings
desired_lines = list(BASE_ENLIVEN_CONF_SETTINGS)
# Add version-specific player animation setting
if self.minetest_version == "5":
desired_lines.append("playeranim.model_version = MTG_4_Nov_2017")
else: # "0.4"
desired_lines.append("playeranim.model_version = MTG_4_Jun_2017")
# Add per-mod settings
for modname, settings in mt_conf_by_mod.items():
desired_lines.append(f"# settings from {modname}")
for k, v in settings.items():
desired_lines.append(f"{k} = {v}")
desired_lines.append("")
if not desired_lines:
return
# Normalize for comparison (strip, remove empty)
desired_set = {line.strip() for line in desired_lines if line.strip()}
# Read existing content
existing_lines = []
if os.path.isfile(path):
with open(path, "r", encoding="utf-8") as f:
existing_lines = [line.rstrip("\n") for line in f]
existing_set = {line.strip() for line in existing_lines if line.strip()}
else:
existing_set = set()
# Find lines to add
to_add = [line for line in desired_lines if line.strip() and line.strip() not in existing_set]
if not to_add:
echo0(f"minetest.conf already contains all desired ENLIVEN settings ({path})")
return
# Write mode
mode = "a" if os.path.isfile(path) else "w"
with open(path, mode, encoding="utf-8") as f:
if mode == "w":
f.write(f"# ENLIVEN subgame recommended settings (Minetest {self.minetest_version} compatibility)\n\n")
for line in to_add:
f.write(line + "\n")
echo0(f"Updated {path} — added {len(to_add)} new line(s)")
def build(self, conf_path: str = None):
self.prepare_target()
self.apply_remove_list()
self.install_all_mods()
self.write_game_conf()
if not conf_path:
conf_path = os.path.join(self.source_game, "minetest.conf.enliven")
self.update_conf(conf_path)
echo0("\nBuild finished.")
echo0(f"Game location: {self.target_game}")
echo0(f"Config : {conf_path}")
echo0("Next steps:")
echo0(" • review & edit the minetest.conf file")
echo0(" • test in Minetest")
def main():
parser = argparse.ArgumentParser(description="Build ENLIVEN subgame from minetest_game")
parser.add_argument("minetest_game_path", help="Path to minetest_game directory")
parser.add_argument("--conf", "-c", dest="conf_path",
help="Path to write/append ENLIVEN minetest.conf settings "
"(default: minetest_game_path/minetest.conf.enliven)")
parser.add_argument("--minetest-version", "-v", dest="minetest_version",
choices=["0.4", "5"], default="5",
help="Target Minetest version compatibility: '5' (default) or '0.4'")
args = parser.parse_args()
try:
builder = GameBuilder(
args.minetest_game_path,
minetest_version=args.minetest_version
)
builder.build(conf_path=args.conf_path)
return 0
except Exception as exc:
echo0(f"ERROR: {exc}", file=sys.stderr)
return 1
if __name__ == "__main__":
sys.exit(main())