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