mirror of
https://github.com/AutismDevelopment/Autism-Client.git
synced 2026-10-28 19:39:48 +00:00
Release the tag update
This commit is contained in:
commit
780a4ac030
1179 changed files with 262845 additions and 0 deletions
136
addon-templates/README.md
Normal file
136
addon-templates/README.md
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
# AUTISM Client Addon Templates
|
||||
|
||||
These are the official addon templates for AUTISM Client.
|
||||
|
||||
Put these in the same repo as the client source. They are standalone Fabric mod projects, but they depend on the AUTISM Client API being published to your local Maven repo first.
|
||||
|
||||
This is not some magical separate SDK. Build the client API locally, then build your addon.
|
||||
|
||||
## Templates
|
||||
|
||||
- `minimal/` - This is the one you should probably start with.
|
||||
|
||||
It gives you the clean basic setup without throwing the entire client architecture at your face. It includes:
|
||||
|
||||
one module
|
||||
one simple macro action
|
||||
one simple wait condition
|
||||
the beginner-friendly API setup
|
||||
|
||||
It is intentionally small. You can actually read it without needing to reverse engineer the entire client.
|
||||
|
||||
- `advanced/` - This is the full reference template.
|
||||
|
||||
Use this when you want to see how the bigger stuff is supposed to be wired. It includes:
|
||||
|
||||
modules
|
||||
macro actions
|
||||
macro conditions
|
||||
presets
|
||||
commands
|
||||
HUD elements
|
||||
events
|
||||
a mixin
|
||||
|
||||
Addon presets are fully supported. They show up in the macro editor `Presets` picker under your addon's
|
||||
own category, the same way addon modules/actions/conditions do.
|
||||
|
||||
Category names are intentionally simple: your addon name is the public category. If an API lets you pass a
|
||||
custom category label, treat that as local organization only. The user-facing UI still groups your stuff under
|
||||
the addon name so the menu does not turn into a junk drawer.
|
||||
|
||||
Use this when you want to see how everything works together, or when the minimal template stops being enough.
|
||||
|
||||
## Build Flow
|
||||
|
||||
From the main AUTISM Client folder, publish the API locally:
|
||||
|
||||
.\gradlew.bat publishToMavenLocal --no-daemon
|
||||
|
||||
Then build the template you want.
|
||||
|
||||
Minimal:
|
||||
|
||||
cd addon-templates\minimal
|
||||
.\gradlew.bat build --no-daemon
|
||||
|
||||
Advanced:
|
||||
|
||||
cd addon-templates\advanced
|
||||
.\gradlew.bat build --no-daemon
|
||||
|
||||
Your addon jar will be in:
|
||||
|
||||
build/libs/
|
||||
|
||||
That is the file you load as the addon.
|
||||
|
||||
## Toolkit
|
||||
|
||||
The toolkit renames addon projects for you so you don't hand-edit Gradle files into a cursed broken
|
||||
state. It's one cross-platform program (`addon-toolkit.py`) with a launcher for each platform - they all
|
||||
do the exact same thing, so pick whatever fits your OS.
|
||||
|
||||
Windows (PowerShell), same commands as before:
|
||||
|
||||
.\addon-templates\addon-toolkit.ps1
|
||||
.\addon-templates\addon-toolkit.ps1 -Action Setup -Template minimal -OutputPath ..\MyAddon
|
||||
.\addon-templates\addon-toolkit.ps1 -Action Scan -ProjectPath ..\MyAddon
|
||||
.\addon-templates\addon-toolkit.ps1 -Action BuildAll
|
||||
|
||||
Linux / macOS / anywhere with Python 3:
|
||||
|
||||
python addon-templates/addon-toolkit.py
|
||||
python addon-templates/addon-toolkit.py setup --template minimal --output ../MyAddon
|
||||
python addon-templates/addon-toolkit.py scan --project ../MyAddon
|
||||
python addon-templates/addon-toolkit.py build-all
|
||||
|
||||
(./addon-templates/addon-toolkit.sh ... is the same thing if you'd rather run a shell script.)
|
||||
|
||||
Through Gradle. Building needs only the JDK; the scaffold/scan tasks call the Python toolkit:
|
||||
|
||||
./gradlew buildAllTemplates # no Python needed
|
||||
./gradlew newAddon -Pname="My Addon" -Ptemplate=minimal -Pout=../MyAddon # needs Python
|
||||
./gradlew scanAddon -Pproject=../MyAddon # needs Python
|
||||
./gradlew validateAddons # needs Python
|
||||
|
||||
Setup asks for:
|
||||
|
||||
addon name
|
||||
output folder
|
||||
version
|
||||
author
|
||||
|
||||
If you leave the version blank, it bumps the current template version by one.
|
||||
|
||||
It also generates the annoying stuff automatically:
|
||||
|
||||
addon id
|
||||
package name
|
||||
Maven group
|
||||
jar name
|
||||
description
|
||||
Fabric entrypoints
|
||||
mixin references
|
||||
|
||||
This matters because half renaming a mod is one of the easiest ways to make Gradle look like it hates you personally.
|
||||
|
||||
If you want to set every value yourself, use:
|
||||
|
||||
.\addon-templates\addon-toolkit.ps1 -Action Setup -Template advanced -Advanced
|
||||
|
||||
Do that only if you actually need control over everything. Or edit the files directly.
|
||||
|
||||
|
||||
## Public Repo Rule
|
||||
|
||||
Commit the source, Gradle wrapper files, and config files.
|
||||
|
||||
Do not commit:
|
||||
|
||||
.gradle/
|
||||
build/
|
||||
|
||||
They are already ignored by the root .gitignore.
|
||||
|
||||
If it is generated trash from your machine, it probably does not belong in the repo.
|
||||
77
addon-templates/addon-toolkit.ps1
Normal file
77
addon-templates/addon-toolkit.ps1
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
param(
|
||||
[ValidateSet("Menu", "Setup", "Scan", "Validate", "PublishApi", "Build", "BuildAll", "Clean")]
|
||||
[string] $Action = "Menu",
|
||||
|
||||
[ValidateSet("minimal", "advanced")]
|
||||
[string] $Template = "minimal",
|
||||
|
||||
[string] $ProjectPath,
|
||||
[string] $OutputPath,
|
||||
[string] $AddonId,
|
||||
[string] $AddonName,
|
||||
[string] $AddonVersion,
|
||||
[string] $Package,
|
||||
[string] $Author,
|
||||
[string] $Description,
|
||||
[string] $MavenGroup,
|
||||
[string] $ArchiveName,
|
||||
[switch] $Advanced,
|
||||
[switch] $DebugStack,
|
||||
[switch] $NoPublish,
|
||||
[switch] $Yes,
|
||||
[switch] $NonInteractive
|
||||
)
|
||||
|
||||
# Forwards to addon-toolkit.py, keeping the old -Action/-Param interface.
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$toolkit = Join-Path $PSScriptRoot "addon-toolkit.py"
|
||||
if (-not (Test-Path -LiteralPath $toolkit)) {
|
||||
Write-Error "addon-toolkit.py not found next to this launcher."
|
||||
exit 1
|
||||
}
|
||||
|
||||
function Resolve-PythonCommand {
|
||||
# Probe --version (not just existence) so we skip Windows' Store-alias stub that exits non-zero.
|
||||
foreach ($candidate in @(@('py', '-3'), @('python'), @('python3'))) {
|
||||
$exe = $candidate[0]
|
||||
if (-not (Get-Command $exe -ErrorAction SilentlyContinue)) { continue }
|
||||
$pre = if ($candidate.Count -gt 1) { $candidate[1..($candidate.Count - 1)] } else { @() }
|
||||
try { & $exe @pre '--version' *> $null } catch { continue }
|
||||
if ($LASTEXITCODE -eq 0) { return , $candidate }
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
$python = Resolve-PythonCommand
|
||||
if (-not $python) {
|
||||
Write-Error "Python 3 was not found on PATH. Install Python 3, or use the Gradle tasks (gradlew newAddon, scanAddon, ...)."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$actionMap = @{
|
||||
Menu = 'menu'; Setup = 'setup'; Scan = 'scan'; Validate = 'validate'
|
||||
PublishApi = 'publish-api'; Build = 'build'; BuildAll = 'build-all'; Clean = 'clean'
|
||||
}
|
||||
|
||||
$pyArgs = @($toolkit, $actionMap[$Action], '--template', $Template)
|
||||
if ($ProjectPath) { $pyArgs += @('--project', $ProjectPath) }
|
||||
if ($OutputPath) { $pyArgs += @('--output', $OutputPath) }
|
||||
if ($AddonId) { $pyArgs += @('--addon-id', $AddonId) }
|
||||
if ($AddonName) { $pyArgs += @('--name', $AddonName) }
|
||||
if ($AddonVersion) { $pyArgs += @('--addon-version', $AddonVersion) }
|
||||
if ($Package) { $pyArgs += @('--package', $Package) }
|
||||
if ($Author) { $pyArgs += @('--author', $Author) }
|
||||
if ($Description) { $pyArgs += @('--description', $Description) }
|
||||
if ($MavenGroup) { $pyArgs += @('--maven-group', $MavenGroup) }
|
||||
if ($ArchiveName) { $pyArgs += @('--archive-name', $ArchiveName) }
|
||||
if ($Advanced) { $pyArgs += '--advanced' }
|
||||
if ($NoPublish) { $pyArgs += '--no-publish' }
|
||||
if ($Yes) { $pyArgs += '--yes' }
|
||||
if ($NonInteractive) { $pyArgs += '--non-interactive' }
|
||||
if ($DebugStack) { $pyArgs += '--debug-stack' }
|
||||
|
||||
$pyExe = $python[0]
|
||||
$pyPre = if ($python.Count -gt 1) { $python[1..($python.Count - 1)] } else { @() }
|
||||
& $pyExe @pyPre @pyArgs
|
||||
exit $LASTEXITCODE
|
||||
759
addon-templates/addon-toolkit.py
Normal file
759
addon-templates/addon-toolkit.py
Normal file
|
|
@ -0,0 +1,759 @@
|
|||
#!/usr/bin/env python3
|
||||
"""AUTISM addon toolkit: scaffold, scan, validate and build addons. Runs anywhere with Python 3.
|
||||
|
||||
python addon-toolkit.py # menu
|
||||
python addon-toolkit.py setup --template minimal --name "My Addon" --output ../MyAddon
|
||||
python addon-toolkit.py scan --project ../MyAddon
|
||||
python addon-toolkit.py build --project ../MyAddon
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
REPO_ROOT = SCRIPT_DIR.parent
|
||||
TEMPLATES = ("minimal", "advanced")
|
||||
TEXT_EXTS = {".java", ".json", ".kts", ".toml", ".properties", ".md", ".yml", ".yaml"}
|
||||
|
||||
|
||||
class ToolkitError(Exception):
|
||||
"""Expected failure, shown without a traceback."""
|
||||
|
||||
|
||||
def info(msg: str) -> None: print(f"[AUTISM] {msg}")
|
||||
def ok(msg: str) -> None: print(f"[OK] {msg}")
|
||||
def warn(msg: str) -> None: print(f"[WARN] {msg}")
|
||||
def bad(msg: str) -> None: print(f"[FAIL] {msg}")
|
||||
|
||||
|
||||
def path_line(label: str, path) -> None:
|
||||
print(f"{label}:")
|
||||
print(str(path))
|
||||
|
||||
|
||||
def failure_item(msg: str) -> None:
|
||||
if re.match(r"^[A-Za-z]:[\\/]", msg) or msg.startswith("/") or re.match(r"^\.{1,2}[\\/]", msg):
|
||||
path_line("Path", msg)
|
||||
else:
|
||||
bad(f" - {msg}")
|
||||
|
||||
|
||||
def is_interactive(args) -> bool:
|
||||
return not getattr(args, "non_interactive", False) and sys.stdin.isatty()
|
||||
|
||||
|
||||
def read_text(path: Path) -> str:
|
||||
return path.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def write_text(path: Path, text: str) -> None:
|
||||
path.write_text(text, encoding="utf-8", newline="")
|
||||
|
||||
|
||||
def to_folder_name(name: str) -> str:
|
||||
if not name or not name.strip():
|
||||
return "MyAddon"
|
||||
parts = re.findall(r"[A-Za-z0-9]+", name)
|
||||
if not parts:
|
||||
return "MyAddon"
|
||||
folder = "".join(p.upper() if len(p) == 1 else p[0].upper() + p[1:] for p in parts)
|
||||
return folder[:48]
|
||||
|
||||
|
||||
def to_package_segment(value: str) -> str:
|
||||
segment = re.sub(r"[^a-z0-9]+", "", value.lower())
|
||||
if not segment:
|
||||
segment = "myaddon"
|
||||
if not segment[0].isalpha():
|
||||
segment = "addon" + segment
|
||||
return segment
|
||||
|
||||
|
||||
def to_mod_id(name: str) -> str:
|
||||
mod_id = re.sub(r"[^a-z0-9_-]+", "-", name.lower()).strip("-_")
|
||||
if not mod_id:
|
||||
return "my-autism-addon"
|
||||
if not mod_id[0].isalpha():
|
||||
mod_id = "addon-" + mod_id
|
||||
if len(mod_id) > 64:
|
||||
mod_id = mod_id[:64].strip("-_")
|
||||
return mod_id
|
||||
|
||||
|
||||
def default_author() -> str:
|
||||
return os.environ.get("USERNAME") or os.environ.get("USER") or "You"
|
||||
|
||||
|
||||
def test_mod_id(value: str) -> bool:
|
||||
return bool(re.match(r"^[a-z][a-z0-9_-]{1,63}$", value or ""))
|
||||
|
||||
|
||||
def test_java_package(value: str) -> bool:
|
||||
return bool(re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)+$", value or ""))
|
||||
|
||||
|
||||
def test_version(value: str) -> bool:
|
||||
return bool(re.match(r"^[0-9A-Za-z][0-9A-Za-z._+-]*$", value or ""))
|
||||
|
||||
|
||||
def step_version(version: str) -> str:
|
||||
if not version or not version.strip():
|
||||
return "1.0.0"
|
||||
m = re.match(r"^(.*?)(\d+)(\D*)$", version)
|
||||
if not m:
|
||||
return version + ".1"
|
||||
prefix, digits, suffix = m.group(1), m.group(2), m.group(3)
|
||||
nxt = str(int(digits) + 1)
|
||||
if len(digits) > 1 and len(nxt) < len(digits):
|
||||
nxt = nxt.rjust(len(digits), "0")
|
||||
return f"{prefix}{nxt}{suffix}"
|
||||
|
||||
|
||||
def get_toml_version(path: Path, key: str) -> str:
|
||||
m = re.search(r'(?m)^\s*' + re.escape(key) + r'\s*=\s*"([^"]+)"', read_text(path))
|
||||
return m.group(1) if m else ""
|
||||
|
||||
|
||||
def set_toml_version(path: Path, key: str, value: str) -> None:
|
||||
text = read_text(path)
|
||||
pattern = r'(?m)^(\s*' + re.escape(key) + r'\s*=\s*)"[^"]*"'
|
||||
if re.search(pattern, text):
|
||||
text = re.sub(pattern, lambda m: m.group(1) + f'"{value}"', text)
|
||||
else:
|
||||
text += f'\n{key} = "{value}"\n'
|
||||
write_text(path, text)
|
||||
|
||||
|
||||
def set_properties_value(path: Path, key: str, value: str) -> None:
|
||||
text = read_text(path)
|
||||
pattern = r"(?m)^" + re.escape(key) + r"=.*$"
|
||||
if re.search(pattern, text):
|
||||
text = re.sub(pattern, f"{key}={value}", text)
|
||||
else:
|
||||
text += f"\n{key}={value}\n"
|
||||
write_text(path, text)
|
||||
|
||||
|
||||
def set_root_project_name(path: Path, name: str) -> None:
|
||||
text = read_text(path)
|
||||
pattern = r'(?m)^(rootProject\.name\s*=\s*)"[^"]*"'
|
||||
if re.search(pattern, text):
|
||||
text = re.sub(pattern, lambda m: m.group(1) + f'"{name}"', text)
|
||||
else:
|
||||
text += f'\nrootProject.name = "{name}"\n'
|
||||
write_text(path, text)
|
||||
|
||||
|
||||
def get_json(path: Path):
|
||||
return json.loads(read_text(path))
|
||||
|
||||
|
||||
def save_json(path: Path, data) -> None:
|
||||
write_text(path, json.dumps(data, indent=2, ensure_ascii=False) + "\n")
|
||||
|
||||
|
||||
def is_under_build_or_gradle(path: Path, project: Path) -> bool:
|
||||
try:
|
||||
parts = path.relative_to(project).parts
|
||||
except ValueError:
|
||||
return False
|
||||
return "build" in parts or ".gradle" in parts
|
||||
|
||||
|
||||
def resolve_full_path(path: str) -> Path | None:
|
||||
if not path or not path.strip():
|
||||
return None
|
||||
p = Path(path)
|
||||
if p.is_absolute():
|
||||
return p.resolve()
|
||||
return (REPO_ROOT / p).resolve()
|
||||
|
||||
|
||||
def is_shipped_template(path: Path) -> bool:
|
||||
full = path.resolve()
|
||||
return any(full == (REPO_ROOT / "addon-templates" / name).resolve() for name in TEMPLATES)
|
||||
|
||||
|
||||
def get_first_java_package(project: Path) -> str:
|
||||
entry = project / "src/main/resources/fabric.mod.json"
|
||||
if entry.is_file():
|
||||
data = get_json(entry)
|
||||
classes = []
|
||||
eps = data.get("entrypoints", {}) if isinstance(data, dict) else {}
|
||||
for kind in ("client", "autism"):
|
||||
if eps.get(kind):
|
||||
classes += list(eps[kind]) if isinstance(eps[kind], list) else [eps[kind]]
|
||||
for cls in classes:
|
||||
m = re.match(r"^(.+)\.[^.]+$", str(cls))
|
||||
if m:
|
||||
return m.group(1)
|
||||
java_root = project / "src/main/java"
|
||||
if java_root.is_dir():
|
||||
for java in sorted(java_root.rglob("*.java")):
|
||||
m = re.search(r"(?m)^\s*package\s+([^;]+);", read_text(java))
|
||||
if m:
|
||||
return m.group(1).strip()
|
||||
return "com.example.addon"
|
||||
|
||||
|
||||
def class_simple_name(class_name: str, fallback: str) -> str:
|
||||
m = re.search(r"\.([^.]+)$", class_name or "")
|
||||
return m.group(1) if m else fallback
|
||||
|
||||
|
||||
def replace_in_project_files(project: Path, old: str, new: str) -> None:
|
||||
if not old or old == new:
|
||||
return
|
||||
for file in project.rglob("*"):
|
||||
if not file.is_file() or file.suffix not in TEXT_EXTS:
|
||||
continue
|
||||
if is_under_build_or_gradle(file, project):
|
||||
continue
|
||||
text = read_text(file)
|
||||
if old in text:
|
||||
write_text(file, text.replace(old, new))
|
||||
|
||||
|
||||
def move_java_package(project: Path, old_pkg: str, new_pkg: str) -> None:
|
||||
if old_pkg == new_pkg:
|
||||
return
|
||||
src_root = project / "src/main/java"
|
||||
old_path = src_root / old_pkg.replace(".", os.sep)
|
||||
new_path = src_root / new_pkg.replace(".", os.sep)
|
||||
if not old_path.is_dir():
|
||||
return
|
||||
if new_path.exists():
|
||||
warn("Package path already exists. Not moving files.")
|
||||
path_line("Path", new_path)
|
||||
return
|
||||
new_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.move(str(old_path), str(new_path))
|
||||
cursor = old_path.parent
|
||||
while src_root in cursor.parents and not any(cursor.iterdir()):
|
||||
parent = cursor.parent
|
||||
cursor.rmdir()
|
||||
cursor = parent
|
||||
|
||||
|
||||
def copy_addon_template(template_name: str, target: Path) -> None:
|
||||
source = REPO_ROOT / "addon-templates" / template_name
|
||||
if not source.is_dir():
|
||||
path_line("Missing template", source)
|
||||
raise ToolkitError("Template not found.")
|
||||
if target.exists():
|
||||
warn("Output already exists. Editing it in place.")
|
||||
path_line("Path", target)
|
||||
return
|
||||
target.mkdir(parents=True, exist_ok=True)
|
||||
for item in source.rglob("*"):
|
||||
if is_under_build_or_gradle(item, source):
|
||||
continue
|
||||
rel = item.relative_to(source)
|
||||
dest = target / rel
|
||||
if item.is_dir():
|
||||
dest.mkdir(parents=True, exist_ok=True)
|
||||
else:
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(item, dest)
|
||||
|
||||
|
||||
def prompt(label: str, default: str, validator=None, interactive: bool = True) -> str:
|
||||
if not interactive:
|
||||
return default
|
||||
while True:
|
||||
suffix = f" [{default}]" if default else ""
|
||||
try:
|
||||
value = input(f"{label}{suffix}: ").strip()
|
||||
except EOFError:
|
||||
return default
|
||||
if not value:
|
||||
value = default
|
||||
if validator is None or validator(value):
|
||||
return value
|
||||
warn("Bad value. Fix it.")
|
||||
|
||||
|
||||
def prompt_version(current: str, interactive: bool) -> str:
|
||||
base = current if current and current.strip() else "1.0.0"
|
||||
nxt = step_version(base)
|
||||
if not interactive:
|
||||
return nxt
|
||||
while True:
|
||||
try:
|
||||
value = input(f"Version current {base}, blank = {nxt}: ").strip()
|
||||
except EOFError:
|
||||
return nxt
|
||||
if not value:
|
||||
return nxt
|
||||
if test_version(value):
|
||||
return value
|
||||
warn("Bad version. Fix it.")
|
||||
|
||||
|
||||
def get_setup_project_path(args, name_for_output: str) -> Path:
|
||||
if args.project and args.project.strip():
|
||||
project = resolve_full_path(args.project)
|
||||
if is_shipped_template(project):
|
||||
raise ToolkitError("No. The shipped templates stay clean. Use --output to copy one, or pick a real addon folder.")
|
||||
return project
|
||||
output = args.output
|
||||
if not output or not output.strip():
|
||||
default_out = str((REPO_ROOT / ".." / to_folder_name(name_for_output)).resolve())
|
||||
output = prompt("Output folder", default_out, lambda v: bool(v and v.strip()), is_interactive(args))
|
||||
target = resolve_full_path(output)
|
||||
if is_shipped_template(target):
|
||||
raise ToolkitError("No. The shipped templates stay clean. Pick a different output folder.")
|
||||
copy_addon_template(args.template, target)
|
||||
return target
|
||||
|
||||
|
||||
def configure_addon(args) -> Path:
|
||||
interactive = is_interactive(args)
|
||||
addon_name = args.addon_name
|
||||
if not addon_name or not addon_name.strip():
|
||||
default_name = "My Advanced Addon" if args.template == "advanced" else "My Addon"
|
||||
addon_name = prompt("Addon name", default_name, lambda v: bool(v and v.strip()), interactive)
|
||||
|
||||
project = get_setup_project_path(args, addon_name)
|
||||
if not project.is_dir():
|
||||
path_line("Missing addon folder", project)
|
||||
raise ToolkitError("Addon project not found.")
|
||||
|
||||
fabric_path = project / "src/main/resources/fabric.mod.json"
|
||||
versions_path = project / "gradle/libs.versions.toml"
|
||||
properties_path = project / "gradle.properties"
|
||||
settings_path = project / "settings.gradle.kts"
|
||||
for required in (fabric_path, versions_path, properties_path, settings_path):
|
||||
if not required.is_file():
|
||||
path_line("Missing file", required)
|
||||
raise ToolkitError("Missing required addon file.")
|
||||
|
||||
fabric = get_json(fabric_path)
|
||||
old_id = str(fabric.get("id", ""))
|
||||
old_package = get_first_java_package(project)
|
||||
old_version = get_toml_version(versions_path, "mod-version")
|
||||
eps = fabric.get("entrypoints", {}) or {}
|
||||
old_client = str((eps.get("client") or [f"{old_package}.Init"])[0])
|
||||
old_addon = str((eps.get("autism") or [f"{old_package}.Addon"])[0])
|
||||
original_mixin = str((fabric.get("mixins") or [""])[0])
|
||||
client_simple = class_simple_name(old_client, "Init")
|
||||
addon_simple = class_simple_name(old_addon, "Addon")
|
||||
|
||||
advanced = args.advanced
|
||||
|
||||
addon_id = args.addon_id
|
||||
if not addon_id or not addon_id.strip():
|
||||
suggested = to_mod_id(addon_name) if "template" in old_id else old_id
|
||||
addon_id = prompt("Addon id", suggested, test_mod_id, interactive) if advanced else suggested
|
||||
|
||||
addon_version = args.addon_version
|
||||
if not addon_version or not addon_version.strip():
|
||||
addon_version = prompt_version(old_version or "1.0.0", interactive)
|
||||
|
||||
package = args.package
|
||||
if not package or not package.strip():
|
||||
suggested_pkg = f"com.{to_package_segment(addon_id)}.addon"
|
||||
package = prompt("Java package", suggested_pkg, test_java_package, interactive) if advanced else suggested_pkg
|
||||
|
||||
maven_group = args.maven_group
|
||||
if not maven_group or not maven_group.strip():
|
||||
maven_group = prompt("Maven group", package, test_java_package, interactive) if advanced else package
|
||||
|
||||
archive_name = args.archive_name
|
||||
if not archive_name or not archive_name.strip():
|
||||
archive_name = prompt("Jar/archive base name", addon_id, test_mod_id, interactive) if advanced else addon_id
|
||||
|
||||
author = args.author
|
||||
if not author or not author.strip():
|
||||
author = prompt("Author", default_author(), lambda v: bool(v and v.strip()), interactive)
|
||||
|
||||
description = args.description
|
||||
if not description or not description.strip():
|
||||
suggested_desc = f"{addon_name} addon for AUTISM Client."
|
||||
description = prompt("Description", suggested_desc, lambda v: bool(v and v.strip()), interactive) if advanced else suggested_desc
|
||||
|
||||
replace_in_project_files(project, old_package, package)
|
||||
replace_in_project_files(project, old_id, addon_id)
|
||||
move_java_package(project, old_package, package)
|
||||
|
||||
fabric = get_json(fabric_path)
|
||||
fabric["id"] = addon_id
|
||||
fabric["name"] = addon_name
|
||||
fabric["version"] = "${version}"
|
||||
fabric["description"] = description
|
||||
fabric["authors"] = [author]
|
||||
fabric.setdefault("entrypoints", {})
|
||||
fabric["entrypoints"]["client"] = [f"{package}.{client_simple}"]
|
||||
fabric["entrypoints"]["autism"] = [f"{package}.{addon_simple}"]
|
||||
|
||||
if fabric.get("mixins"):
|
||||
old_mixin_name = original_mixin or str(fabric["mixins"][0])
|
||||
new_mixin_name = f"{addon_id}.mixins.json"
|
||||
resource_dir = project / "src/main/resources"
|
||||
old_mixin = resource_dir / old_mixin_name
|
||||
new_mixin = resource_dir / new_mixin_name
|
||||
if old_mixin.is_file() and old_mixin != new_mixin:
|
||||
shutil.move(str(old_mixin), str(new_mixin))
|
||||
if new_mixin.is_file():
|
||||
mixin_json = get_json(new_mixin)
|
||||
mixin_json["package"] = f"{package}.mixin"
|
||||
save_json(new_mixin, mixin_json)
|
||||
fabric["mixins"] = [new_mixin_name]
|
||||
|
||||
save_json(fabric_path, fabric)
|
||||
set_toml_version(versions_path, "mod-version", addon_version)
|
||||
set_properties_value(properties_path, "maven_group", maven_group)
|
||||
set_properties_value(properties_path, "archives_base_name", archive_name)
|
||||
set_root_project_name(settings_path, addon_id)
|
||||
|
||||
ok("Addon rewritten.")
|
||||
path_line("Addon folder", project)
|
||||
print(f" id: {addon_id}")
|
||||
print(f" name: {addon_name}")
|
||||
print(f" version: {addon_version}")
|
||||
print(f" package: {package}")
|
||||
print()
|
||||
print("Next:")
|
||||
print(f" python addon-templates/addon-toolkit.py build --project \"{project}\"")
|
||||
path_line("Jar folder", project / "build" / "libs")
|
||||
return project
|
||||
|
||||
|
||||
def test_addon_project(project: Path, allow_template_names: bool = False):
|
||||
failures: list[str] = []
|
||||
warnings: list[str] = []
|
||||
|
||||
if not project.is_dir():
|
||||
failures.append("Project folder does not exist.")
|
||||
failures.append(str(project))
|
||||
return failures, warnings
|
||||
|
||||
required = [
|
||||
"build.gradle.kts", "settings.gradle.kts", "gradle.properties",
|
||||
"gradle/libs.versions.toml", "src/main/resources/fabric.mod.json",
|
||||
]
|
||||
for item in required:
|
||||
if not (project / item).is_file():
|
||||
failures.append(f"Missing {item}")
|
||||
if not (project / "gradlew.bat").is_file() and not (project / "gradlew").is_file():
|
||||
failures.append("Missing gradlew / gradlew.bat")
|
||||
|
||||
fabric_path = project / "src/main/resources/fabric.mod.json"
|
||||
if fabric_path.is_file():
|
||||
try:
|
||||
fabric = get_json(fabric_path)
|
||||
if not test_mod_id(str(fabric.get("id", ""))):
|
||||
failures.append(f"fabric.mod.json id is invalid: {fabric.get('id')}")
|
||||
if not str(fabric.get("name", "")).strip():
|
||||
failures.append("fabric.mod.json name is empty.")
|
||||
eps = fabric.get("entrypoints", {}) or {}
|
||||
if not eps.get("autism"):
|
||||
failures.append("Missing autism entrypoint in fabric.mod.json.")
|
||||
else:
|
||||
for entry in eps["autism"]:
|
||||
cls = project / ("src/main/java/" + str(entry).replace(".", "/") + ".java")
|
||||
if not cls.is_file():
|
||||
failures.append(f"Autism entrypoint class file is missing: {entry}")
|
||||
if not (fabric.get("depends", {}) or {}).get("autism"):
|
||||
failures.append("fabric.mod.json must depend on autism.")
|
||||
if not allow_template_names:
|
||||
if re.search(r"template|example", str(fabric.get("id", ""))):
|
||||
warnings.append("Addon id still looks like a template/example.")
|
||||
if re.search(r"Template|Example", str(fabric.get("name", ""))):
|
||||
warnings.append("Addon name still looks like a template/example.")
|
||||
if "You" in (fabric.get("authors") or []):
|
||||
warnings.append("Author is still 'You'.")
|
||||
except json.JSONDecodeError as exc:
|
||||
failures.append(f"fabric.mod.json is not valid JSON: {exc}")
|
||||
|
||||
versions_path = project / "gradle/libs.versions.toml"
|
||||
if versions_path.is_file():
|
||||
if not test_version(get_toml_version(versions_path, "mod-version")):
|
||||
failures.append("Addon mod-version is missing or invalid.")
|
||||
if not get_toml_version(versions_path, "autism").strip():
|
||||
failures.append("AUTISM dependency version is missing from gradle/libs.versions.toml.")
|
||||
|
||||
if (project / ".gradle").exists():
|
||||
warnings.append(".gradle cache exists locally. Ignored; do not upload it.")
|
||||
if (project / "build").exists():
|
||||
warnings.append("build output exists locally. Ignored; do not upload it.")
|
||||
if (project / ".github").exists():
|
||||
warnings.append(".github exists inside this addon project. Fine for a copied template, but it is not root repo CI unless you move it.")
|
||||
|
||||
leftovers = (
|
||||
"autism-minimal-addon-template", "autism-advanced-addon-template",
|
||||
"AUTISM Minimal Addon Template", "AUTISM Advanced Addon Template",
|
||||
)
|
||||
for file in project.rglob("*"):
|
||||
if not file.is_file() or file.suffix not in TEXT_EXTS or is_under_build_or_gradle(file, project):
|
||||
continue
|
||||
text = read_text(file)
|
||||
if re.search(r"[ÂÃâ]", text):
|
||||
warnings.append(f"Mojibake-looking text in {file}")
|
||||
if not allow_template_names:
|
||||
for leftover in leftovers:
|
||||
if leftover in text:
|
||||
warnings.append(f"Template leftover '{leftover}' in {file}")
|
||||
break
|
||||
return failures, warnings
|
||||
|
||||
|
||||
def get_addon_project_path(args) -> Path:
|
||||
if args.project and args.project.strip():
|
||||
return resolve_full_path(args.project)
|
||||
if args.output and args.output.strip():
|
||||
target = resolve_full_path(args.output)
|
||||
copy_addon_template(args.template, target)
|
||||
return target
|
||||
return REPO_ROOT / "addon-templates" / args.template
|
||||
|
||||
|
||||
def scan_addon(args) -> None:
|
||||
project = get_addon_project_path(args)
|
||||
failures, warnings = test_addon_project(project)
|
||||
info("Scanned addon project.")
|
||||
path_line("Addon folder", project)
|
||||
for w in warnings:
|
||||
warn(f"Heads up: {w}")
|
||||
if failures:
|
||||
bad("Scan found broken stuff:")
|
||||
for f in failures:
|
||||
failure_item(f)
|
||||
raise ToolkitError("Scan failed.")
|
||||
ok("Scan clean.")
|
||||
|
||||
|
||||
def validate_addon_system(args) -> None:
|
||||
failures: list[str] = []
|
||||
warnings: list[str] = []
|
||||
|
||||
root_required = [
|
||||
"src/main/java/autismclient/addons/AddonManager.java",
|
||||
"src/main/java/autismclient/api/AutismAddon.java",
|
||||
"src/main/java/autismclient/api/SimpleAddon.java",
|
||||
"src/main/java/autismclient/api/AutismAddons.java",
|
||||
"src/main/java/autismclient/api/ApiVersion.java",
|
||||
"src/main/java/autismclient/api/module/SimpleModule.java",
|
||||
"src/main/java/autismclient/api/macro/SimpleAction.java",
|
||||
"src/main/java/autismclient/api/macro/SimpleCondition.java",
|
||||
"src/main/java/autismclient/api/macro/MacroActionRegistry.java",
|
||||
"src/main/java/autismclient/api/macro/MacroPresetRegistry.java",
|
||||
"src/main/java/autismclient/api/hud/HudElements.java",
|
||||
"src/main/java/autismclient/api/event/AddonEvents.java",
|
||||
"src/main/java/autismclient/gui/screen/AutismAddonsScreen.java",
|
||||
"src/main/java/autismclient/util/macro/MissingAddonAction.java",
|
||||
"addon-templates/README.md",
|
||||
"addon-templates/addon-toolkit.py",
|
||||
"addon-templates/addon-toolkit.ps1",
|
||||
"addon-templates/addon-toolkit.sh",
|
||||
]
|
||||
for f in root_required:
|
||||
if not (REPO_ROOT / f).is_file():
|
||||
failures.append(f"Missing {f}")
|
||||
|
||||
gitignore = REPO_ROOT / ".gitignore"
|
||||
if gitignore.is_file():
|
||||
ignore_text = read_text(gitignore)
|
||||
for pattern in ("!/src/", "!/addon-templates/", "!/addon-templates/**",
|
||||
"/addon-templates/**/build/", "/addon-templates/**/.gradle/", "/mc-src*/"):
|
||||
if pattern not in ignore_text:
|
||||
failures.append(f".gitignore is missing {pattern}")
|
||||
else:
|
||||
failures.append("Missing .gitignore")
|
||||
|
||||
main_version = get_toml_version(REPO_ROOT / "gradle/libs.versions.toml", "mod-version")
|
||||
for template_name in TEMPLATES:
|
||||
project = REPO_ROOT / "addon-templates" / template_name
|
||||
t_failures, t_warnings = test_addon_project(project, allow_template_names=True)
|
||||
failures += [f"{template_name} template: {x}" for x in t_failures]
|
||||
warnings += [f"{template_name} template: {x}" for x in t_warnings]
|
||||
target_version = get_toml_version(project / "gradle/libs.versions.toml", "autism")
|
||||
if main_version and target_version and main_version != target_version:
|
||||
failures.append(f"{template_name} template targets AUTISM {target_version}, main client is {main_version}.")
|
||||
|
||||
for w in warnings:
|
||||
warn(f"Heads up: {w}")
|
||||
if failures:
|
||||
bad("Addon system is broken:")
|
||||
for f in failures:
|
||||
failure_item(f)
|
||||
raise ToolkitError("Addon system validation failed.")
|
||||
ok("Addon system clean.")
|
||||
|
||||
|
||||
def run_gradle(project: Path, gradle_args: list[str]) -> None:
|
||||
wrapper = project / ("gradlew.bat" if os.name == "nt" else "gradlew")
|
||||
if not wrapper.is_file():
|
||||
path_line("Missing Gradle wrapper", wrapper)
|
||||
raise ToolkitError("Missing Gradle wrapper.")
|
||||
cmd = [str(wrapper)] + gradle_args
|
||||
if os.name != "nt" and not os.access(wrapper, os.X_OK):
|
||||
cmd = ["sh", str(wrapper)] + gradle_args
|
||||
result = subprocess.run(cmd, cwd=str(project))
|
||||
if result.returncode != 0:
|
||||
path_line("Gradle project", project)
|
||||
raise ToolkitError(f"Gradle failed with exit code {result.returncode}.")
|
||||
|
||||
|
||||
def publish_api(args) -> None:
|
||||
info("Publishing client API to Maven Local.")
|
||||
run_gradle(REPO_ROOT, ["publishToMavenLocal", "--no-daemon"])
|
||||
ok("Client API published.")
|
||||
|
||||
|
||||
def build_addon(args) -> None:
|
||||
project = get_addon_project_path(args)
|
||||
if not project.is_dir():
|
||||
path_line("Addon folder", project)
|
||||
raise ToolkitError("Wrong addon folder.")
|
||||
if not (project / "gradlew.bat").is_file() and not (project / "gradlew").is_file():
|
||||
path_line("Addon folder", project)
|
||||
raise ToolkitError("No Gradle wrapper there. Wrong addon folder.")
|
||||
if not args.no_publish:
|
||||
publish_api(args)
|
||||
info("Building addon.")
|
||||
path_line("Addon folder", project)
|
||||
run_gradle(project, ["build", "--no-daemon"])
|
||||
ok("Addon built.")
|
||||
path_line("Jar folder", project / "build" / "libs")
|
||||
|
||||
|
||||
def build_all_templates(args) -> None:
|
||||
if not args.no_publish:
|
||||
publish_api(args)
|
||||
for template_name in TEMPLATES:
|
||||
project = REPO_ROOT / "addon-templates" / template_name
|
||||
info(f"Building {template_name} template.")
|
||||
run_gradle(project, ["build", "--no-daemon"])
|
||||
ok(f"{template_name} template built.")
|
||||
|
||||
|
||||
def clean_addon(args) -> None:
|
||||
project = get_addon_project_path(args)
|
||||
interactive = is_interactive(args)
|
||||
for folder in (".gradle", "build"):
|
||||
path = project / folder
|
||||
if path.exists():
|
||||
confirmed = args.yes or (interactive and input(f"Remove {path} ? Type YES: ").strip() == "YES")
|
||||
if confirmed:
|
||||
shutil.rmtree(path)
|
||||
ok("Deleted local build junk.")
|
||||
path_line("Path", path)
|
||||
|
||||
|
||||
def menu(args) -> None:
|
||||
last_path = ""
|
||||
while True:
|
||||
print()
|
||||
print("AUTISM ADDON KIT")
|
||||
print("1. New addon")
|
||||
print("2. Build addon")
|
||||
print("3. Scan addon")
|
||||
print("4. Test shipped templates")
|
||||
print("5. Publish client API")
|
||||
print("6. Delete local build junk")
|
||||
print("0. Leave")
|
||||
try:
|
||||
choice = input("> ").strip()
|
||||
except EOFError:
|
||||
return
|
||||
try:
|
||||
if choice == "1":
|
||||
args.template = prompt("Template minimal/advanced", args.template, lambda v: v in TEMPLATES, True)
|
||||
args.project = ""
|
||||
args.output = ""
|
||||
last_path = str(configure_addon(args))
|
||||
elif choice == "2":
|
||||
default_path = last_path or str((REPO_ROOT / ".." / to_folder_name("")).resolve())
|
||||
args.project = prompt("Addon folder", default_path, lambda v: bool(v and v.strip()), True)
|
||||
build_addon(args)
|
||||
elif choice == "3":
|
||||
default_path = last_path or str((REPO_ROOT / ".." / to_folder_name("")).resolve())
|
||||
args.project = prompt("Addon folder", default_path, lambda v: bool(v and v.strip()), True)
|
||||
scan_addon(args)
|
||||
elif choice == "4":
|
||||
validate_addon_system(args)
|
||||
build_all_templates(args)
|
||||
elif choice == "5":
|
||||
publish_api(args)
|
||||
elif choice == "6":
|
||||
default_path = last_path or str((REPO_ROOT / ".." / to_folder_name("")).resolve())
|
||||
args.project = prompt("Addon folder", default_path, lambda v: bool(v and v.strip()), True)
|
||||
clean_addon(args)
|
||||
elif choice == "0":
|
||||
return
|
||||
else:
|
||||
warn("Bad choice.")
|
||||
except ToolkitError as exc:
|
||||
bad(str(exc))
|
||||
try:
|
||||
input("Press Enter. The error stays here until you do")
|
||||
except EOFError:
|
||||
return
|
||||
|
||||
|
||||
def normalize_action(value: str) -> str:
|
||||
v = value.strip().lower()
|
||||
return {"publishapi": "publish-api", "buildall": "build-all"}.get(v, v)
|
||||
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(description="AUTISM addon toolkit.")
|
||||
parser.add_argument("action", nargs="?", default="menu", type=normalize_action,
|
||||
choices=["menu", "setup", "scan", "validate", "publish-api", "build", "build-all", "clean"])
|
||||
parser.add_argument("--template", type=str.lower, choices=list(TEMPLATES), default="minimal")
|
||||
parser.add_argument("--project", "--project-path", dest="project")
|
||||
parser.add_argument("--output", "--output-path", dest="output")
|
||||
parser.add_argument("--addon-id", dest="addon_id")
|
||||
parser.add_argument("--name", "--addon-name", dest="addon_name")
|
||||
parser.add_argument("--addon-version", dest="addon_version")
|
||||
parser.add_argument("--package")
|
||||
parser.add_argument("--author")
|
||||
parser.add_argument("--description")
|
||||
parser.add_argument("--maven-group", dest="maven_group")
|
||||
parser.add_argument("--archive-name", dest="archive_name")
|
||||
parser.add_argument("--advanced", action="store_true")
|
||||
parser.add_argument("--no-publish", action="store_true", dest="no_publish")
|
||||
parser.add_argument("--yes", action="store_true")
|
||||
parser.add_argument("--non-interactive", action="store_true", dest="non_interactive")
|
||||
parser.add_argument("--debug-stack", action="store_true", dest="debug_stack")
|
||||
return parser
|
||||
|
||||
|
||||
ACTIONS = {
|
||||
"menu": menu,
|
||||
"setup": configure_addon,
|
||||
"scan": scan_addon,
|
||||
"validate": validate_addon_system,
|
||||
"publish-api": publish_api,
|
||||
"build": build_addon,
|
||||
"build-all": build_all_templates,
|
||||
"clean": clean_addon,
|
||||
}
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
args = build_parser().parse_args(argv)
|
||||
try:
|
||||
ACTIONS[args.action](args)
|
||||
return 0
|
||||
except ToolkitError as exc:
|
||||
bad(str(exc))
|
||||
if args.debug_stack:
|
||||
raise
|
||||
return 1
|
||||
except KeyboardInterrupt:
|
||||
print()
|
||||
return 130
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
20
addon-templates/addon-toolkit.sh
Executable file
20
addon-templates/addon-toolkit.sh
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env sh
|
||||
# Forwards to addon-toolkit.py. Example: ./addon-toolkit.sh setup --template minimal --output ../MyAddon
|
||||
set -e
|
||||
|
||||
DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
|
||||
TOOLKIT="$DIR/addon-toolkit.py"
|
||||
if [ ! -f "$TOOLKIT" ]; then
|
||||
echo "addon-toolkit.py not found next to this launcher." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Probe --version (not just existence) so we skip Windows' Store-alias stub that exits 49.
|
||||
for PY in python3 python py; do
|
||||
if "$PY" --version >/dev/null 2>&1; then
|
||||
exec "$PY" "$TOOLKIT" "$@"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Python 3 was not found on PATH. Install Python 3, or use the Gradle tasks (./gradlew newAddon, scanAddon, ...)." >&2
|
||||
exit 1
|
||||
15
addon-templates/advanced/.editorconfig
Normal file
15
addon-templates/advanced/.editorconfig
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{json,yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
45
addon-templates/advanced/.github/workflows/build.yml
vendored
Normal file
45
addon-templates/advanced/.github/workflows/build.yml
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up JDK 25
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 25
|
||||
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
# NOTE: the build needs the AUTISM Client API artifact (com.autismclient:autism) to be resolvable.
|
||||
# mavenLocal() is empty on a fresh CI runner, so make it available one of these ways:
|
||||
# - publish AUTISM to a maven repo your build.gradle.kts lists (GitHub Packages, a maven server), or
|
||||
# - commit the AUTISM jar into a `libs/` folder and add `flatDir { dirs("libs") }` to repositories.
|
||||
- name: Build
|
||||
run: ./gradlew build --stacktrace
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: addon-jar
|
||||
path: build/libs/*.jar
|
||||
|
||||
- name: Release dev build
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: dev
|
||||
name: Dev Build
|
||||
prerelease: true
|
||||
files: build/libs/*.jar
|
||||
6
addon-templates/advanced/.gitignore
vendored
Normal file
6
addon-templates/advanced/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.gradle/
|
||||
build/
|
||||
run/
|
||||
*.log
|
||||
.idea/
|
||||
*.iml
|
||||
95
addon-templates/advanced/README.md
Normal file
95
addon-templates/advanced/README.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# AUTISM Advanced Addon Template
|
||||
|
||||
A standalone reference addon for AUTISM Client. It registers one of each supported extension point, so
|
||||
you can copy it when you need the full API surface.
|
||||
|
||||
Start small: keep `ExampleModule`, build once, then add macro actions, commands, HUD elements, events,
|
||||
or mixins as needed.
|
||||
|
||||
## What It Demonstrates
|
||||
|
||||
| Extension point | File |
|
||||
| --- | --- |
|
||||
| Addon lifecycle | `ExampleAddon` |
|
||||
| Module | `modules/ExampleModule` |
|
||||
| Macro action | `macro/ExampleSayAction` |
|
||||
| Macro condition | `macro/ExampleHeightCondition` + `macro/ExampleCondition` |
|
||||
| Macro preset | `macro/ExamplePresets` |
|
||||
| Command | `commands/ExampleCommand` |
|
||||
| HUD element | `hud/ExampleHud` |
|
||||
| Event hooks | `events/ExampleEvents` |
|
||||
| Mixin | `mixin/ExampleMixin` + `autism-advanced-addon-template.mixins.json` |
|
||||
|
||||
Presets are first-class addon entries. `ExamplePresets` appears in the macro editor `Presets` picker
|
||||
under this addon's category, and it can insert any normal stack of macro actions.
|
||||
|
||||
Your modules, macro actions, conditions and presets automatically group under a category named after
|
||||
your addon from `fabric.mod.json`. Custom categories are scoped under your addon so they cannot collide
|
||||
with built-ins or another addon.
|
||||
|
||||
## Build
|
||||
|
||||
1. In the AUTISM Client project, publish the API to your local Maven repo:
|
||||
```sh
|
||||
./gradlew publishToMavenLocal
|
||||
```
|
||||
2. Make sure the `autism` version in `gradle/libs.versions.toml` matches what you published.
|
||||
3. Build this addon:
|
||||
```sh
|
||||
./gradlew build
|
||||
```
|
||||
|
||||
The addon jar lands in `build/libs/`.
|
||||
|
||||
## Run
|
||||
|
||||
Drop both jars into your `mods/` folder:
|
||||
|
||||
- the AUTISM Client jar;
|
||||
- this addon's jar from `build/libs/`.
|
||||
|
||||
## Make It Yours
|
||||
|
||||
Recommended (any OS, from the AUTISM Client repo root):
|
||||
|
||||
```sh
|
||||
python addon-templates/addon-toolkit.py setup --template advanced --output ../MyAdvancedAddon
|
||||
```
|
||||
|
||||
Windows PowerShell works the same: `.\addon-templates\addon-toolkit.ps1 -Action Setup -Template advanced -OutputPath ..\MyAdvancedAddon`. See `../README.md` for the Gradle (`./gradlew newAddon`) and `.sh` variants.
|
||||
|
||||
The toolkit asks for the addon name, output folder, version, and author. Blank version bumps the current
|
||||
template version by one patch step. It derives the boring stuff and rewrites Gradle files, package paths,
|
||||
`fabric.mod.json`, entrypoints, and the mixin config in one shot.
|
||||
|
||||
Manual path:
|
||||
|
||||
1. Rename the package `com.example.addon`.
|
||||
2. Change `ExampleAddon.ID`.
|
||||
3. Update `src/main/resources/fabric.mod.json`: `id`, `name`, `entrypoints`, `mixins`, and `autism:color`.
|
||||
4. Rename `autism-advanced-addon-template.mixins.json` and update its `package`.
|
||||
5. Keep `apiVersion()` and the `autism` dependency range honest for the client version you target.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```text
|
||||
addon-templates/advanced/
|
||||
|-- gradle/
|
||||
|-- src/main/java/com/example/addon/
|
||||
| |-- ExampleAddon.java
|
||||
| |-- ExampleInit.java
|
||||
| |-- modules/ExampleModule.java
|
||||
| |-- macro/ExampleSayAction.java
|
||||
| |-- macro/ExampleHeightCondition.java
|
||||
| |-- macro/ExampleCondition.java
|
||||
| |-- macro/ExamplePresets.java
|
||||
| |-- commands/ExampleCommand.java
|
||||
| |-- hud/ExampleHud.java
|
||||
| |-- events/ExampleEvents.java
|
||||
| `-- mixin/ExampleMixin.java
|
||||
|-- src/main/resources/
|
||||
| |-- fabric.mod.json
|
||||
| `-- autism-advanced-addon-template.mixins.json
|
||||
|-- build.gradle.kts
|
||||
`-- gradle.properties
|
||||
```
|
||||
67
addon-templates/advanced/build.gradle.kts
Normal file
67
addon-templates/advanced/build.gradle.kts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
plugins {
|
||||
alias(libs.plugins.fabric.loom)
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName = properties["archives_base_name"] as String
|
||||
version = libs.versions.mod.version.get()
|
||||
group = properties["maven_group"] as String
|
||||
}
|
||||
|
||||
repositories {
|
||||
// AUTISM Client is consumed from your local Maven repo. In the AUTISM project run:
|
||||
// ./gradlew publishToMavenLocal
|
||||
mavenLocal()
|
||||
maven("https://maven.fabricmc.net/") { name = "Fabric" }
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Mirrors the AUTISM Client build: official Mojang mappings are implicit for the configured version, so there is no
|
||||
// explicit `mappings(...)` line and dependencies use plain `implementation`.
|
||||
minecraft(libs.minecraft)
|
||||
implementation(libs.fabric.loader)
|
||||
implementation(libs.fabric.api)
|
||||
|
||||
// The AUTISM Client API (published to mavenLocal from the AUTISM project).
|
||||
implementation(libs.autism)
|
||||
}
|
||||
|
||||
// Turns an exact Minecraft version (e.g. "26.2") into a compatible range ("~26.2") so the addon
|
||||
// keeps loading across patch releases instead of pinning one exact build.
|
||||
fun toMinecraftCompat(version: String): String {
|
||||
val m = Regex("""^(\d+)\.(\d+)(?:\.(\d+))?$""").matchEntire(version)
|
||||
?: return version
|
||||
val (year, drop, _) = m.destructured
|
||||
return "~$year.$drop"
|
||||
}
|
||||
|
||||
tasks {
|
||||
processResources {
|
||||
val propertyMap = mapOf(
|
||||
"version" to project.version,
|
||||
"mc_version" to libs.versions.minecraft.get(),
|
||||
"mc_compat" to toMinecraftCompat(libs.versions.minecraft.get()),
|
||||
"fabric_api_version" to libs.versions.fabric.api.get(),
|
||||
"autism_api_version" to libs.versions.autism.get()
|
||||
)
|
||||
inputs.properties(propertyMap)
|
||||
filteringCharset = "UTF-8"
|
||||
filesMatching("fabric.mod.json") {
|
||||
expand(propertyMap)
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_25
|
||||
targetCompatibility = JavaVersion.VERSION_25
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(25))
|
||||
}
|
||||
}
|
||||
|
||||
withType<JavaCompile> {
|
||||
options.encoding = "UTF-8"
|
||||
options.release.set(25)
|
||||
}
|
||||
}
|
||||
7
addon-templates/advanced/gradle.properties
Normal file
7
addon-templates/advanced/gradle.properties
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
org.gradle.jvmargs=-Xmx2G
|
||||
org.gradle.parallel=true
|
||||
org.gradle.caching=true
|
||||
org.gradle.configuration-cache=false
|
||||
|
||||
maven_group=com.example.addon
|
||||
archives_base_name=autism-advanced-addon-template
|
||||
23
addon-templates/advanced/gradle/libs.versions.toml
Normal file
23
addon-templates/advanced/gradle/libs.versions.toml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# In this repo these are auto-synced from the client: run `gradlew syncAddonTemplateVersions` (it also runs
|
||||
# on publishToMavenLocal), which pins minecraft/fabric-loader/fabric-api/loom/autism to the client's
|
||||
# gradle/libs.versions.toml. In a copied-out addon, set them to the AUTISM Client release you build against.
|
||||
[versions]
|
||||
mod-version = "1.0.0"
|
||||
|
||||
minecraft = "26.2"
|
||||
fabric-loader = "0.19.3"
|
||||
fabric-api = "0.152.2+26.2"
|
||||
# The AUTISM Client release you compile against. The generated fabric.mod.json depends on `>=` this version, so a
|
||||
# built addon keeps loading on newer hosts in the same major line (the API-version handshake gates real breaks).
|
||||
autism = "4.4"
|
||||
|
||||
loom = "1.17.11"
|
||||
|
||||
[libraries]
|
||||
minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" }
|
||||
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
||||
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
||||
autism = { module = "com.autismclient:autism", version.ref = "autism" }
|
||||
|
||||
[plugins]
|
||||
fabric-loom = { id = "net.fabricmc.fabric-loom", version.ref = "loom" }
|
||||
BIN
addon-templates/advanced/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
addon-templates/advanced/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
7
addon-templates/advanced/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
addon-templates/advanced/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
248
addon-templates/advanced/gradlew
vendored
Executable file
248
addon-templates/advanced/gradlew
vendored
Executable file
|
|
@ -0,0 +1,248 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
93
addon-templates/advanced/gradlew.bat
vendored
Normal file
93
addon-templates/advanced/gradlew.bat
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
9
addon-templates/advanced/settings.gradle.kts
Normal file
9
addon-templates/advanced/settings.gradle.kts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
pluginManagement {
|
||||
repositories {
|
||||
maven("https://maven.fabricmc.net/") { name = "Fabric" }
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "autism-advanced-addon-template"
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package com.example.addon;
|
||||
|
||||
import autismclient.api.AutismAddon;
|
||||
import autismclient.api.AutismAddons;
|
||||
import autismclient.api.ApiVersion;
|
||||
import autismclient.api.macro.MacroActionEntry;
|
||||
|
||||
import com.example.addon.commands.ExampleCommand;
|
||||
import com.example.addon.events.ExampleEvents;
|
||||
import com.example.addon.hud.ExampleHud;
|
||||
import com.example.addon.macro.ExampleHeightCondition;
|
||||
import com.example.addon.macro.ExamplePresets;
|
||||
import com.example.addon.macro.ExampleSayAction;
|
||||
import com.example.addon.modules.ExampleModule;
|
||||
|
||||
// Addon entrypoint (the "autism" entrypoint in fabric.mod.json). Start small:
|
||||
// register only ExampleModule first, launch the client, then add macro/actions/HUD/events as needed.
|
||||
// Modules, actions, conditions and presets all auto-group under a category named after this addon.
|
||||
public final class ExampleAddon extends AutismAddon {
|
||||
public static final String ID = "autism-advanced-addon-template";
|
||||
|
||||
@Override
|
||||
public int apiVersion() {
|
||||
return ApiVersion.CURRENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
AutismAddons.modules().register(new ExampleModule());
|
||||
|
||||
// Action: appears in the "add action" picker.
|
||||
AutismAddons.macroActions().register(MacroActionEntry.local("say", ExampleSayAction::new)
|
||||
.picker("Say", "Send a chat message.")
|
||||
.build());
|
||||
|
||||
// Condition: appears in the "add condition" picker (it waits).
|
||||
AutismAddons.macroActions().register(MacroActionEntry.local("wait-height", ExampleHeightCondition::new)
|
||||
.condition("Wait Height", "Wait until the player is at or above a Y level.")
|
||||
.build());
|
||||
|
||||
AutismAddons.presets().register("Reach Height", "Wait for Y 80, then say.",
|
||||
ExamplePresets::reachHeightThenSay);
|
||||
|
||||
AutismAddons.commands().register(new ExampleCommand());
|
||||
AutismAddons.hud().register(new ExampleHud());
|
||||
ExampleEvents.register();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPackage() {
|
||||
return "com.example.addon";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.example.addon;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
|
||||
// Fabric's required client entrypoint. The addon's real work is in ExampleAddon.
|
||||
public final class ExampleInit implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.example.addon.commands;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
|
||||
// A Brigadier command. Users run it with the command prefix, e.g. .example or .example hi.
|
||||
public final class ExampleCommand extends Command {
|
||||
public ExampleCommand() {
|
||||
super("example", "Example addon command.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
AutismClientMessaging.sendPrefixed("\u00a7dExample addon command works!");
|
||||
return SUCCESS;
|
||||
});
|
||||
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("text", StringArgumentType.greedyString())
|
||||
.executes(ctx -> {
|
||||
AutismClientMessaging.sendPrefixed("\u00a7dYou said: \u00a7f" + StringArgumentType.getString(ctx, "text"));
|
||||
return SUCCESS;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.example.addon.events;
|
||||
|
||||
import autismclient.api.AutismAddons;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
|
||||
// Event hooks. Register listeners once in onInitialize().
|
||||
// Available: onTick, onPacketSend (return true to cancel), onPacketReceive, onGameJoin, onGameLeft.
|
||||
public final class ExampleEvents {
|
||||
private ExampleEvents() {}
|
||||
|
||||
public static void register() {
|
||||
AutismAddons.events().onGameJoin(() ->
|
||||
AutismClientMessaging.sendPrefixed("\u00a7a[Example] joined a world!"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.example.addon.hud;
|
||||
|
||||
import com.example.addon.ExampleAddon;
|
||||
import autismclient.api.hud.HudElementProvider;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
|
||||
// A HUD element. Report your size via width()/height(); draw in render() with the GuiGraphicsExtractor
|
||||
// (ctx.text / ctx.fill). The defaultX/Y/anchor/enabled are used until the user moves it in the HUD editor.
|
||||
public final class ExampleHud implements HudElementProvider {
|
||||
private static final int MIN_WIDTH = 94;
|
||||
private static final int TEXT_PAD = 2;
|
||||
|
||||
@Override public String id() { return ExampleAddon.ID + ":clock"; }
|
||||
@Override public String label() { return "Example Clock"; }
|
||||
@Override public String description() { return "Shows the in-game tick."; }
|
||||
|
||||
@Override
|
||||
public int width() {
|
||||
Font font = Minecraft.getInstance().font;
|
||||
String text = text();
|
||||
int textWidth = font != null ? font.width(text) : text.length() * 6;
|
||||
return Math.max(MIN_WIDTH, textWidth + TEXT_PAD * 2);
|
||||
}
|
||||
|
||||
@Override public int height() { return 10; }
|
||||
|
||||
@Override
|
||||
public void render(GuiGraphicsExtractor ctx, Font font, int x, int y, float alpha) {
|
||||
ctx.text(font, text(), x + TEXT_PAD, y, 0xFFFFD7FF);
|
||||
}
|
||||
|
||||
private static String text() {
|
||||
long tick = Minecraft.getInstance().level != null ? Minecraft.getInstance().level.getGameTime() : 0L;
|
||||
return "Addon tick: " + tick;
|
||||
}
|
||||
|
||||
@Override public boolean defaultEnabled() { return false; }
|
||||
@Override public String defaultAnchor() { return "TOP_LEFT"; }
|
||||
@Override public int defaultX() { return 4; }
|
||||
@Override public int defaultY() { return 120; }
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.example.addon.macro;
|
||||
|
||||
import autismclient.api.macro.AddonCondition;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
// A reusable predicate: implement check(). A waiting step blocks on it via ctx.awaitCondition(...).
|
||||
public final class ExampleCondition extends AddonCondition {
|
||||
private final double minY;
|
||||
|
||||
public ExampleCondition(double minY) {
|
||||
this.minY = minY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean check(Minecraft mc) {
|
||||
return mc.player != null && mc.player.getY() >= minY;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package com.example.addon.macro;
|
||||
|
||||
import com.example.addon.ExampleAddon;
|
||||
import autismclient.api.macro.ActionSchema;
|
||||
import autismclient.api.macro.AddonContextAction;
|
||||
import autismclient.api.macro.MacroExecutionContext;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
// A macro condition: a step that waits. Extend AddonContextAction and block in run(), e.g. with
|
||||
// ctx.awaitCondition(...). Register it with .condition(...) so it lands in the condition picker.
|
||||
public final class ExampleHeightCondition extends AddonContextAction {
|
||||
public static final String TYPE_ID = ExampleAddon.ID + ":wait-height";
|
||||
|
||||
public double minY = 64.0;
|
||||
|
||||
public ExampleHeightCondition() {
|
||||
super(TYPE_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(MacroExecutionContext ctx) throws InterruptedException {
|
||||
ctx.setStatus("Waiting until Y >= " + minY);
|
||||
ctx.awaitCondition(new ExampleCondition(minY));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void save(CompoundTag tag) {
|
||||
putDouble(tag, "minY", minY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void load(CompoundTag tag) {
|
||||
minY = getDouble(tag, "minY", minY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ActionSchema schema() {
|
||||
return ActionSchema.builder().decimal("minY", "Min Y").decRange(-64, 320).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Wait until Y >= " + minY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIcon() {
|
||||
return "Y";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.example.addon.macro;
|
||||
|
||||
import autismclient.util.macro.MacroAction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
// Presets insert a ready-made stack of steps. Return a fresh list each call; don't reuse instances.
|
||||
public final class ExamplePresets {
|
||||
private ExamplePresets() {}
|
||||
|
||||
// Wait until the player is high up, then announce it.
|
||||
public static List<MacroAction> reachHeightThenSay() {
|
||||
List<MacroAction> stack = new ArrayList<>();
|
||||
|
||||
ExampleHeightCondition waitForHeight = new ExampleHeightCondition();
|
||||
waitForHeight.minY = 80.0;
|
||||
stack.add(waitForHeight);
|
||||
|
||||
ExampleSayAction say = new ExampleSayAction();
|
||||
say.message = "Reached Y 80!";
|
||||
stack.add(say);
|
||||
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package com.example.addon.macro;
|
||||
|
||||
import com.example.addon.ExampleAddon;
|
||||
import autismclient.api.macro.ActionSchema;
|
||||
import autismclient.api.macro.AddonAction;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
// A macro action: runs once when the step is reached. Extend AddonAction and pass a namespaced id.
|
||||
public final class ExampleSayAction extends AddonAction {
|
||||
public static final String TYPE_ID = ExampleAddon.ID + ":say";
|
||||
|
||||
public String message = "Hello from an addon!";
|
||||
|
||||
public ExampleSayAction() {
|
||||
super(TYPE_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(Minecraft mc) {
|
||||
AutismClientMessaging.sendPrefixed("\u00a7d[Example] " + message);
|
||||
}
|
||||
|
||||
// Field keys here, in load(), and in the schema must match.
|
||||
@Override
|
||||
protected void save(CompoundTag tag) {
|
||||
putString(tag, "message", message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void load(CompoundTag tag) {
|
||||
message = getString(tag, "message", message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ActionSchema schema() {
|
||||
return ActionSchema.builder().text("message", "Say").build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Say: " + message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIcon() {
|
||||
return "S";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.example.addon.mixin;
|
||||
|
||||
import com.example.addon.ExampleAddon;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.main.GameConfig;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
// Example mixin. List your mixin classes in autism-advanced-addon-template.mixins.json (referenced from
|
||||
// fabric.mod.json). An @Inject handler matches the target method's params, then CallbackInfo.
|
||||
@Mixin(Minecraft.class)
|
||||
public abstract class ExampleMixin {
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void onClientInit(GameConfig gameConfig, CallbackInfo ci) {
|
||||
org.slf4j.LoggerFactory.getLogger(ExampleAddon.ID).info("Hello from ExampleMixin!");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.example.addon.modules;
|
||||
|
||||
import com.example.addon.ExampleAddon;
|
||||
import autismclient.api.module.BoolSetting;
|
||||
import autismclient.api.module.EnumSetting;
|
||||
import autismclient.api.module.IntSetting;
|
||||
import autismclient.modules.Module;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
// A toggleable module built on the typed setting API. Declare each setting as a final field with
|
||||
// add(new XSetting(...)) and read it at runtime with field.get() — no string ids, no casting. An EnumSetting
|
||||
// is backed by a real enum, so reads are compiler-checked (style.get() == Style.LOUD). Visibility is a plain
|
||||
// predicate over other settings. With no category, the module auto-lands under a menu column named after your addon.
|
||||
public final class ExampleModule extends Module {
|
||||
public enum Style { FRIENDLY, LOUD }
|
||||
|
||||
private final BoolSetting greet = add(new BoolSetting("greet", "Greet on enable", true)
|
||||
.group("General"));
|
||||
private final IntSetting amount = add(new IntSetting("amount", "Amount", 3, 1, 10, 1)
|
||||
.group("General"));
|
||||
private final EnumSetting<Style> style = add(new EnumSetting<>("style", "Style", Style.FRIENDLY, Style.values())
|
||||
.group("Display"));
|
||||
private final BoolSetting shout = add(new BoolSetting("shout", "Shout in caps", false)
|
||||
.group("Display")
|
||||
.visibleWhen(() -> style.get() == Style.LOUD));
|
||||
|
||||
public ExampleModule() {
|
||||
super(ExampleAddon.ID + ":example", "Example", "Demonstrates an AUTISM addon module.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
if (greet.get()) {
|
||||
String msg = "[Example] enabled - amount " + amount.get() + ", style " + style.get();
|
||||
if (shout.get()) msg = msg.toUpperCase(Locale.ROOT);
|
||||
AutismClientMessaging.sendPrefixed("§b" + msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"required": true,
|
||||
"package": "com.example.addon.mixin",
|
||||
"compatibilityLevel": "JAVA_25",
|
||||
"client": [
|
||||
"ExampleMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
37
addon-templates/advanced/src/main/resources/fabric.mod.json
Normal file
37
addon-templates/advanced/src/main/resources/fabric.mod.json
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "autism-advanced-addon-template",
|
||||
"version": "${version}",
|
||||
"name": "AUTISM Advanced",
|
||||
"description": "Reference addon for AUTISM Client: a module, macro action, preset, condition, command, HUD element, and event hook.",
|
||||
"authors": [
|
||||
"You"
|
||||
],
|
||||
"custom": {
|
||||
"autism:color": "255,80,80",
|
||||
"modmenu": {
|
||||
"parent": {
|
||||
"id": "autism"
|
||||
}
|
||||
}
|
||||
},
|
||||
"environment": "client",
|
||||
"entrypoints": {
|
||||
"client": [
|
||||
"com.example.addon.ExampleInit"
|
||||
],
|
||||
"autism": [
|
||||
"com.example.addon.ExampleAddon"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"autism-advanced-addon-template.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"java": ">=25",
|
||||
"minecraft": "${mc_compat}",
|
||||
"fabricloader": ">=0.19.3",
|
||||
"fabric-api": ">=${fabric_api_version}",
|
||||
"autism": ">=${autism_api_version}"
|
||||
}
|
||||
}
|
||||
15
addon-templates/minimal/.editorconfig
Normal file
15
addon-templates/minimal/.editorconfig
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{json,yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
6
addon-templates/minimal/.gitignore
vendored
Normal file
6
addon-templates/minimal/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.gradle/
|
||||
build/
|
||||
run/
|
||||
*.log
|
||||
.idea/
|
||||
*.iml
|
||||
44
addon-templates/minimal/README.md
Normal file
44
addon-templates/minimal/README.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# AUTISM Minimal Addon Template
|
||||
|
||||
Smallest useful AUTISM addon example:
|
||||
|
||||
- one module;
|
||||
- one macro action;
|
||||
- one macro condition.
|
||||
|
||||
This template uses the easy API (`SimpleAddon`, `SimpleModule`) and the one-call `registerSimpleAction` / `registerSimpleCondition` helpers.
|
||||
Use `../advanced` when you need commands, HUD elements, events, raw custom macro serialization, or mixins.
|
||||
|
||||
## Build
|
||||
|
||||
1. From the AUTISM Client project, publish the API locally:
|
||||
```powershell
|
||||
.\gradlew.bat publishToMavenLocal --no-daemon
|
||||
```
|
||||
2. From this folder, build the addon:
|
||||
```powershell
|
||||
.\gradlew.bat build --no-daemon
|
||||
```
|
||||
|
||||
The jar lands in `build/libs/`.
|
||||
|
||||
## Customize
|
||||
|
||||
Recommended (any OS, from the AUTISM Client repo root):
|
||||
|
||||
```sh
|
||||
python addon-templates/addon-toolkit.py setup --template minimal --output ../MyAddon
|
||||
```
|
||||
|
||||
Windows PowerShell works the same: `.\addon-templates\addon-toolkit.ps1 -Action Setup -Template minimal -OutputPath ..\MyAddon`. See `../README.md` for the Gradle (`./gradlew newAddon`) and `.sh` variants.
|
||||
|
||||
The toolkit asks for the addon name, output folder, version, and author. Blank version bumps the current
|
||||
template version by one patch step. It derives the boring stuff and rewrites Gradle files, package paths,
|
||||
`fabric.mod.json`, and entrypoints in one shot.
|
||||
|
||||
Manual path:
|
||||
|
||||
1. Rename package `com.example.minimal`.
|
||||
2. Change `MinimalAddon.ID`.
|
||||
3. Update `src/main/resources/fabric.mod.json`.
|
||||
4. Change names/descriptions in `MinimalAddon`.
|
||||
67
addon-templates/minimal/build.gradle.kts
Normal file
67
addon-templates/minimal/build.gradle.kts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
plugins {
|
||||
alias(libs.plugins.fabric.loom)
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName = properties["archives_base_name"] as String
|
||||
version = libs.versions.mod.version.get()
|
||||
group = properties["maven_group"] as String
|
||||
}
|
||||
|
||||
repositories {
|
||||
// AUTISM Client is consumed from your local Maven repo. In the AUTISM project run:
|
||||
// ./gradlew publishToMavenLocal
|
||||
mavenLocal()
|
||||
maven("https://maven.fabricmc.net/") { name = "Fabric" }
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Mirrors the AUTISM Client build: official Mojang mappings are implicit for the configured version, so there is no
|
||||
// explicit `mappings(...)` line and dependencies use plain `implementation`.
|
||||
minecraft(libs.minecraft)
|
||||
implementation(libs.fabric.loader)
|
||||
implementation(libs.fabric.api)
|
||||
|
||||
// The AUTISM Client API (published to mavenLocal from the AUTISM project).
|
||||
implementation(libs.autism)
|
||||
}
|
||||
|
||||
// Turns an exact Minecraft version (e.g. "26.2") into a compatible range ("~26.2") so the addon
|
||||
// keeps loading across patch releases instead of pinning one exact build.
|
||||
fun toMinecraftCompat(version: String): String {
|
||||
val m = Regex("""^(\d+)\.(\d+)(?:\.(\d+))?$""").matchEntire(version)
|
||||
?: return version
|
||||
val (year, drop, _) = m.destructured
|
||||
return "~$year.$drop"
|
||||
}
|
||||
|
||||
tasks {
|
||||
processResources {
|
||||
val propertyMap = mapOf(
|
||||
"version" to project.version,
|
||||
"mc_version" to libs.versions.minecraft.get(),
|
||||
"mc_compat" to toMinecraftCompat(libs.versions.minecraft.get()),
|
||||
"fabric_api_version" to libs.versions.fabric.api.get(),
|
||||
"autism_api_version" to libs.versions.autism.get()
|
||||
)
|
||||
inputs.properties(propertyMap)
|
||||
filteringCharset = "UTF-8"
|
||||
filesMatching("fabric.mod.json") {
|
||||
expand(propertyMap)
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_25
|
||||
targetCompatibility = JavaVersion.VERSION_25
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(25))
|
||||
}
|
||||
}
|
||||
|
||||
withType<JavaCompile> {
|
||||
options.encoding = "UTF-8"
|
||||
options.release.set(25)
|
||||
}
|
||||
}
|
||||
7
addon-templates/minimal/gradle.properties
Normal file
7
addon-templates/minimal/gradle.properties
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
org.gradle.jvmargs=-Xmx2G
|
||||
org.gradle.parallel=true
|
||||
org.gradle.caching=true
|
||||
org.gradle.configuration-cache=false
|
||||
|
||||
maven_group=com.example.addon
|
||||
archives_base_name=autism-minimal-addon-template
|
||||
23
addon-templates/minimal/gradle/libs.versions.toml
Normal file
23
addon-templates/minimal/gradle/libs.versions.toml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# In this repo these are auto-synced from the client: run `gradlew syncAddonTemplateVersions` (it also runs
|
||||
# on publishToMavenLocal), which pins minecraft/fabric-loader/fabric-api/loom/autism to the client's
|
||||
# gradle/libs.versions.toml. In a copied-out addon, set them to the AUTISM Client release you build against.
|
||||
[versions]
|
||||
mod-version = "1.0.0"
|
||||
|
||||
minecraft = "26.2"
|
||||
fabric-loader = "0.19.3"
|
||||
fabric-api = "0.152.2+26.2"
|
||||
# The AUTISM Client release you compile against. The generated fabric.mod.json depends on `>=` this version, so a
|
||||
# built addon keeps loading on newer hosts in the same major line (the API-version handshake gates real breaks).
|
||||
autism = "4.4"
|
||||
|
||||
loom = "1.17.11"
|
||||
|
||||
[libraries]
|
||||
minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" }
|
||||
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
||||
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
||||
autism = { module = "com.autismclient:autism", version.ref = "autism" }
|
||||
|
||||
[plugins]
|
||||
fabric-loom = { id = "net.fabricmc.fabric-loom", version.ref = "loom" }
|
||||
BIN
addon-templates/minimal/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
addon-templates/minimal/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
7
addon-templates/minimal/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
addon-templates/minimal/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
248
addon-templates/minimal/gradlew
vendored
Executable file
248
addon-templates/minimal/gradlew
vendored
Executable file
|
|
@ -0,0 +1,248 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
93
addon-templates/minimal/gradlew.bat
vendored
Normal file
93
addon-templates/minimal/gradlew.bat
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
9
addon-templates/minimal/settings.gradle.kts
Normal file
9
addon-templates/minimal/settings.gradle.kts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
pluginManagement {
|
||||
repositories {
|
||||
maven("https://maven.fabricmc.net/") { name = "Fabric" }
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "autism-minimal-addon-template"
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.example.minimal;
|
||||
|
||||
import autismclient.api.ApiVersion;
|
||||
import autismclient.api.SimpleAddon;
|
||||
import autismclient.api.module.SimpleModule;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
|
||||
public final class MinimalAddon extends SimpleAddon {
|
||||
public static final String ID = "autism-minimal-addon-template";
|
||||
|
||||
public MinimalAddon() {
|
||||
super(ApiVersion.CURRENT, "com.example.minimal");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize() {
|
||||
SimpleModule module = new SimpleModule("hello-module", "Hello Module", "Tiny example addon module.")
|
||||
.addBool("greet", "Greet On Enable", true)
|
||||
.addText("message", "Message", "Hello from my addon")
|
||||
.onEnabled(self -> {
|
||||
if (self.getBool("greet")) {
|
||||
AutismClientMessaging.sendPrefixed("\u00a7a" + self.getText("message"));
|
||||
}
|
||||
});
|
||||
registerModule(module);
|
||||
|
||||
registerSimpleAction("say-hello", "Say Hello", "Send a short addon message.", "S", mc ->
|
||||
AutismClientMessaging.sendPrefixed("\u00a7aHello from the minimal addon!"));
|
||||
|
||||
registerSimpleCondition("wait-player", "Wait Player", "Wait until the local player exists.",
|
||||
"Waiting for player", "P", mc -> mc.player != null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.example.minimal;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
|
||||
public final class MinimalInit implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {}
|
||||
}
|
||||
34
addon-templates/minimal/src/main/resources/fabric.mod.json
Normal file
34
addon-templates/minimal/src/main/resources/fabric.mod.json
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "autism-minimal-addon-template",
|
||||
"version": "${version}",
|
||||
"name": "AUTISM Minimal Addon Template",
|
||||
"description": "Small AUTISM Client addon example: one module, one macro action, and one macro condition.",
|
||||
"authors": [
|
||||
"You"
|
||||
],
|
||||
"custom": {
|
||||
"autism:color": "80,220,120",
|
||||
"modmenu": {
|
||||
"parent": {
|
||||
"id": "autism"
|
||||
}
|
||||
}
|
||||
},
|
||||
"environment": "client",
|
||||
"entrypoints": {
|
||||
"client": [
|
||||
"com.example.minimal.MinimalInit"
|
||||
],
|
||||
"autism": [
|
||||
"com.example.minimal.MinimalAddon"
|
||||
]
|
||||
},
|
||||
"depends": {
|
||||
"java": ">=25",
|
||||
"minecraft": "${mc_compat}",
|
||||
"fabricloader": ">=0.19.3",
|
||||
"fabric-api": ">=${fabric_api_version}",
|
||||
"autism": ">=${autism_api_version}"
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue