HyCodeYourTale

Manifest Structure

Manifest Structure

Detailní dokumentace k manifest.json struktuře pluginu Hytale.

---

Přehled Architektury

PluginManifest (definice pluginu)

├── PluginIdentifier (Group:Name)

├── Semver (verzování)

├── SemverRange (rozsah verzí pro závislosti)

└── AuthorInfo (informace o autorech)

---

PluginManifest

Hlavní třída pro definici pluginu:

public class PluginManifest {
private String group; // Skupina (např. "Hytale", "MyCompany")
private String name; // Název pluginu
private Semver version; // Verze
@Nullable
private String description; // Popis
@Nonnull
private List authors; // Autoři
@Nullable
private String website; // Website URL
@Nullable
private String main; // Entry point třída
private SemverRange serverVersion; // Požadovaná verze serveru

// Závislosti
@Nonnull
private Map dependencies;
@Nonnull
private Map optionalDependencies;
@Nonnull
private Map loadBefore;

// Sub-pluginy (pro mody s více pluginy)
@Nonnull
private List subPlugins;

// Flags
private boolean disabledByDefault = false;
private boolean includesAssetPack = false;
}

---

JSON Struktura

Kompletní Manifest

{
"Group": "MyCompany",
"Name": "MyPlugin",
"Version": "1.0.0",
"Description": "Detailní popis pluginu a jeho funkcí",
"Authors": [
{
"Name": "John Doe",
"Email": "john@example.com",
"Website": "https://johndoe.dev"
}
],
"Website": "https://github.com/mycompany/myplugin",
"Main": "com.mycompany.myplugin.MyPlugin",
"ServerVersion": ">=0.4.0",
"Dependencies": {
"Hytale:DamageModule": "*",
"Hytale:TeleportPlugin": ">=1.0.0"
},
"OptionalDependencies": {
"Hytale:BedsPlugin": "*"
},
"LoadBefore": {
"OtherPlugin:UISystem": "*"
},
"SubPlugins": [],
"DisabledByDefault": false,
"IncludesAssetPack": false
}

Minimální Manifest

{
"Group": "MyCompany",
"Name": "MyPlugin",
"Version": "1.0.0",
"Main": "com.mycompany.myplugin.MyPlugin"
}

---

Pole Manifestu

Povinná Pole

| Pole | Typ | Popis |
|------|-----|-------|
| Group | String | Skupina/namespace (např. "Hytale", "MyCompany") |
| Name | String | Název pluginu (unikátní v rámci skupiny) |
| Version | Semver | Verze ve formátu semver |
| Main | String | Plně kvalifikovaná třída pluginu |

Volitelná Pole

| Pole | Typ | Default | Popis |
|------|-----|---------|-------|
| Description | String | null | Popis pluginu |
| Authors | Array | [] | Seznam autorů |
| Website | String | null | URL webu/repozitáře |
| ServerVersion | SemverRange | null | Požadovaná verze serveru |
| Dependencies | Object | {} | Povinné závislosti |
| OptionalDependencies | Object | {} | Volitelné závislosti |
| LoadBefore | Object | {} | Načíst před těmito pluginy |
| SubPlugins | Array | [] | Vnořené pluginy |
| DisabledByDefault | Boolean | false | Defaultně vypnutý |
| IncludesAssetPack | Boolean | false | Obsahuje asset pack |

---

PluginIdentifier

Unikátní identifikátor pluginu ve formátu Group:Name:

public class PluginIdentifier {
@Nonnull
private final String group;
@Nonnull
private final String name;

public PluginIdentifier(@Nonnull String group, @Nonnull String name) {
this.group = group;
this.name = name;
}

public PluginIdentifier(@Nonnull PluginManifest manifest) {
this(manifest.getGroup(), manifest.getName());
}

@Nonnull
@Override
public String toString() {
return this.group + ":" + this.name;
}

@Nonnull
public static PluginIdentifier fromString(@Nonnull String str) {
String[] split = str.split(":");
if (split.length != 2) {
throw new IllegalArgumentException("String does not match :");
}
return new PluginIdentifier(split[0], split[1]);
}
}

Příklady PluginIdentifier

| String | Group | Name |
|--------|-------|------|
| "Hytale:DamageModule" | Hytale | DamageModule |
| "Hytale:TeleportPlugin" | Hytale | TeleportPlugin |
| "MyCompany:MyPlugin" | MyCompany | MyPlugin |

---

AuthorInfo

Informace o autorovi:

public class AuthorInfo {
@Nonnull
private String name;
@Nullable
private String email;
@Nullable
private String website;
}

JSON Formát

{
"Authors": [
{
"Name": "Primary Author",
"Email": "author@example.com",
"Website": "https://author.dev"
},
{
"Name": "Contributor",
"Email": "contributor@example.com"
}
]
}

---

SubPlugins

Mod může obsahovat více pluginů:

{
"Group": "MyMod",
"Name": "Core",
"Version": "1.0.0",
"Main": "com.mymod.core.CorePlugin",
"SubPlugins": [
{
"Name": "Economy",
"Main": "com.mymod.economy.EconomyPlugin",
"Dependencies": {
"MyMod:Core": "*"
}
},
{
"Name": "Combat",
"Main": "com.mymod.combat.CombatPlugin",
"Dependencies": {
"MyMod:Core": "*",
"Hytale:DamageModule": "*"
}
}
]
}

Dědičnost v SubPlugins

Sub-pluginy dědí od parent manifestu:

public void inherit(@Nonnull PluginManifest manifest) {
if (this.group == null) {
this.group = manifest.group; // Dědí Group
}

if (this.version == null) {
this.version = manifest.version; // Dědí Version
}

if (this.description == null) {
this.description = manifest.description; // Dědí Description
}

if (this.authors.isEmpty()) {
this.authors = manifest.authors; // Dědí Authors
}

if (this.website == null) {
this.website = manifest.website; // Dědí Website
}

if (!this.disabledByDefault) {
this.disabledByDefault = manifest.disabledByDefault;
}

// Automaticky přidá závislost na parent
this.dependencies.put(
new PluginIdentifier(manifest),
SemverRange.fromString(manifest.version.toString())
);
}

---

Speciální Flags

DisabledByDefault

Plugin se defaultně nenačte:

{
"DisabledByDefault": true
}

Použití:

  • Debug pluginy

  • Experimentální funkce

  • Volitelné rozšíření
  • Aktivace v server configu:

    {
    "Mods": {
    "MyCompany:DebugPlugin": {
    "Enabled": true
    }
    }
    }

    IncludesAssetPack

    Plugin obsahuje asset pack:

    {
    "IncludesAssetPack": true
    }

    Asset pack struktura:

    my-plugin.jar
    ├── manifest.json
    ├── com/mycompany/... (Java třídy)
    └── assets/ (Asset pack)
    ├── pack.json
    ├── Models/
    ├── Textures/
    └── UI/

    ---

    CoreBuilder

    Pro builtin Hytale pluginy:

    public static class CoreBuilder {
    private static final String CORE_GROUP = "Hytale";
    private static final Semver CORE_VERSION = ManifestUtil.getVersion();

    @Nonnull
    public static CoreBuilder corePlugin(@Nonnull Class pluginClass) {
    return new CoreBuilder(
    "Hytale",
    pluginClass.getSimpleName(),
    CORE_VERSION,
    pluginClass.getName()
    );
    }

    @Nonnull
    public CoreBuilder description(@Nonnull String description) {
    this.description = description;
    return this;
    }

    @Nonnull
    @SafeVarargs
    public final CoreBuilder depends(@Nonnull Class... dependencies) {
    for (Class dependency : dependencies) {
    this.dependencies.put(
    new PluginIdentifier("Hytale", dependency.getSimpleName()),
    SemverRange.WILDCARD
    );
    }
    return this;
    }

    @Nonnull
    @SafeVarargs
    public final CoreBuilder optDepends(@Nonnull Class... dependencies) {
    for (Class optDep : dependencies) {
    this.optionalDependencies.put(
    new PluginIdentifier("Hytale", optDep.getSimpleName()),
    SemverRange.WILDCARD
    );
    }
    return this;
    }

    @Nonnull
    @SafeVarargs
    public final CoreBuilder loadsBefore(@Nonnull Class... plugins) {
    for (Class plugin : plugins) {
    this.loadBefore.put(
    new PluginIdentifier("Hytale", plugin.getSimpleName()),
    SemverRange.WILDCARD
    );
    }
    return this;
    }

    @Nonnull
    public PluginManifest build() {
    return new PluginManifest(
    this.group,
    this.name,
    this.version,
    this.description,
    Collections.emptyList(),
    null,
    this.main,
    null,
    this.dependencies,
    this.optionalDependencies,
    this.loadBefore,
    Collections.emptyList(),
    false
    );
    }
    }

    Příklad Použití CoreBuilder

    // V TeleportPlugin
    @Override
    @Nonnull
    public PluginManifest getPluginManifest() {
    return PluginManifest.corePlugin(TeleportPlugin.class)
    .description("Provides teleportation commands and warp system")
    .depends(DamageModule.class) // Vyžaduje DamageModule
    .optDepends(BedsPlugin.class) // Volitelně používá BedsPlugin
    .build();
    }

    ---

    CODEC

    PluginManifest má definovaný codec pro serializaci/deserializaci:

    @Nonnull
    public static final Codec CODEC = BUILDER
    .append(new KeyedCodec<>("Group", Codec.STRING),
    (m, o) -> m.group = o, m -> m.group)
    .add()
    .append(new KeyedCodec<>("Name", Codec.STRING),
    (m, o) -> m.name = o, m -> m.name)
    .addValidator(Validators.nonNull()) // Name je povinné
    .add()
    .append(new KeyedCodec<>("Version", Semver.CODEC),
    (m, o) -> m.version = o, m -> m.version)
    .add()
    // ... další pole
    .build();

    ---

    Validace

    Povinná Pole

    - Name musí být non-null
  • Main musí být validní třída

Validace Závislostí

private void validatePluginDeps(PendingLoadPlugin plugin, Map pending) {
// Kontrola verze serveru
SemverRange serverVersionRange = plugin.getManifest().getServerVersion();
if (serverVersionRange != null && !serverVersionRange.satisfies(serverVersion)) {
throw new MissingPluginDependencyException("Server version mismatch");
}

// Kontrola závislostí
for (Entry entry : plugin.getManifest().getDependencies().entrySet()) {
PluginIdentifier identifier = entry.getKey();
SemverRange expectedVersion = entry.getValue();

PluginManifest dependency = findDependency(identifier, pending);
if (dependency == null) {
throw new MissingPluginDependencyException("Dependency not found: " + identifier);
}

if (!dependency.getVersion().satisfies(expectedVersion)) {
throw new MissingPluginDependencyException("Version mismatch for: " + identifier);
}
}
}

---

Shrnutí

| Pole | Povinné | Typ | Popis |
|------|---------|-----|-------|
| Group | Ne* | String | Skupina/namespace |
| Name | Ano | String | Název pluginu |
| Version | Ne* | Semver | Verze |
| Main | Ne | String | Entry point |
| Description | Ne | String | Popis |
| Authors | Ne | Array | Autoři |
| Website | Ne | String | URL |
| ServerVersion | Ne | SemverRange | Požadovaná verze serveru |
| Dependencies | Ne | Object | Povinné závislosti |
| OptionalDependencies | Ne | Object | Volitelné závislosti |
| LoadBefore | Ne | Object | Načíst před |
| SubPlugins | Ne | Array | Vnořené pluginy |
| DisabledByDefault | Ne | Boolean | Defaultně vypnutý |
| IncludesAssetPack | Ne | Boolean | Obsahuje assety |

*Pro sub-pluginy se dědí od parent

Last updated: 20. ledna 2026