Version: 1.0.0
Last Updated: 2026-01-28
Status: ✅ Active
This guide explains how the Argonath Systems architecture integrates with Hytale, including the critical Accessor Pattern that enables platform-agnostic development. Understanding this integration is essential for all mod developers.
Argonath Systems uses a layered architecture that isolates all Hytale-specific code to a single adapter module:
┌───────────────────────────────────────────────────────┐
│ YOUR MOD │
│ (Uses ONLY framework interfaces - no hytale.*) │
├───────────────────────────────────────────────────────┤
│ FRAMEWORK LAYER (04-05-xx) │
│ • framework-quest • framework-npc │
│ • framework-ui • framework-objective │
│ (Platform-agnostic business logic) │
├───────────────────────────────────────────────────────┤
│ ACCESSOR API (02-framework-accessor) │
│ • EntityAccessor • PlayerAccessor │
│ • EventAccessor • WorldAccessor │
│ (Interface definitions - no implementations) │
├───────────────────────────────────────────────────────┤
│ HYTALE ADAPTER (02-adapter-hytale) │
│ 🚨 ONLY MODULE WITH hytale.* IMPORTS 🚨 │
│ (Implements all Accessor interfaces) │
├───────────────────────────────────────────────────────┤
│ HYTALE ENGINE │
│ (Game runtime - com.hypixel.hytale.*) │
└───────────────────────────────────────────────────────┘
Accessors are interfaces that abstract game engine operations. Your mod depends on accessor interfaces, never on Hytale classes directly.
| Accessor | Purpose |
|---|---|
EntityAccessor |
Entity creation, modification, metadata |
PlayerAccessor |
Player data, inventory, stats |
EventAccessor |
Event subscription and dispatch |
WorldAccessor |
World data, block operations, spawning |
ItemAccessor |
Item creation and manipulation |
SoundAccessor |
Sound playback |
RegistryAccessor |
Game asset registries |
LootTableAccessor |
Loot table querying |
// ✅ CORRECT - Use accessor interfaces
public class MyQuestManager {
private final EntityAccessor entityAccessor;
private final EventAccessor eventAccessor;
public MyQuestManager(EntityAccessor entityAccessor, EventAccessor eventAccessor) {
this.entityAccessor = entityAccessor;
this.eventAccessor = eventAccessor;
}
public void spawnQuestNPC(String npcId, LocationData location) {
entityAccessor.spawnEntity("hytale:human", location);
}
}
// ❌ FORBIDDEN - Never import Hytale classes in frameworks/mods
import com.hypixel.hytale.entity.Entity; // NEVER DO THIS
Accessors are injected at runtime by the adapter:
// In your plugin initialization
public class MyModPlugin extends JavaPluginInit {
@Override
public void onEnable(JavaPlugin plugin) {
// Get accessor provider
AccessorProvider provider = AccessorProvider.getInstance();
// Get accessor implementations
EntityAccessor entityAccessor = provider.getEntityAccessor();
EventAccessor eventAccessor = provider.getEventAccessor();
// Create your services
MyQuestManager questManager = new MyQuestManager(entityAccessor, eventAccessor);
}
}
The 02-adapter-hytale module is the only place where Hytale-specific code exists.
02-adapter-hytale/
└── src/main/java/com/argonathsystems/adapter/hytale/
├── HytaleAdapterPlugin.java # Main plugin entry
├── HytaleAdapterProvider.java # AccessorProvider implementation
├── accessor/ # Accessor implementations
│ ├── HytaleEntityAccessor.java
│ ├── HytalePlayerAccessor.java
│ ├── HytaleEventAccessor.java
│ ├── HytaleWorldAccessor.java
│ └── HytaleItemAccessor.java
├── converter/ # Type converters
│ ├── EntityConverter.java
│ ├── ItemConverter.java
│ └── LocationConverter.java
└── integration/ # Quest/NPC integration helpers
└── QuestFormatConverter.java
The adapter converts between Hytale types and platform-agnostic DTOs:
// Hytale types (ONLY in adapter)
import com.hypixel.hytale.entity.Player;
import com.hypixel.hytale.item.ItemStack;
import com.hypixel.hytale.world.Location;
// Platform-agnostic DTOs (used everywhere else)
import com.argonathsystems.framework.accessorapi.dto.PlayerData;
import com.argonathsystems.framework.accessorapi.dto.ItemData;
import com.argonathsystems.framework.accessorapi.dto.LocationData;
Conversion Examples:
// In HytaleEntityAccessor (adapter layer only)
public PlayerData getPlayerData(UUID playerId) {
Player hytalePlayer = server.getPlayer(playerId);
return new PlayerData(
hytalePlayer.getUniqueId(),
hytalePlayer.getName(),
hytalePlayer.getHealth(),
hytalePlayer.getMaxHealth(),
convertLocation(hytalePlayer.getLocation())
);
}
private LocationData convertLocation(Location loc) {
return new LocationData(
loc.getWorld().getName(),
loc.getX(),
loc.getY(),
loc.getZ(),
loc.getYaw(),
loc.getPitch()
);
}
┌──────────────┐ ┌─────────────────┐ ┌──────────────────┐
│ Hytale Event │────▶│ Adapter Layer │────▶│ Accessor Event │
│ (Native) │ │ (Converts) │ │ (Platform-agnostic)
└──────────────┘ └─────────────────┘ └──────────────────┘
│
▼
┌──────────────────┐
│ Your Event │
│ Handlers │
└──────────────────┘
// In HytaleEventAdapter (adapter layer)
public class HytaleEventAdapter {
public void init(HytaleServer server, EventAccessor eventAccessor) {
// Listen to Hytale native events
server.getEventBus().register(PlayerInteractEntityEvent.class, nativeEvent -> {
// Convert to platform-agnostic event
NPCInteractEvent accessorEvent = new NPCInteractEvent(
nativeEvent.getPlayer().getUniqueId(),
nativeEvent.getEntity().getId().toString(),
InteractionType.CLICK,
System.currentTimeMillis()
);
// Dispatch to accessor event bus
eventAccessor.dispatch(accessorEvent);
});
}
}
// In your mod (framework layer)
public class NPCInteractionHandler {
public NPCInteractionHandler(EventAccessor eventAccessor) {
// Register for platform-agnostic events
eventAccessor.register(NPCInteractEvent.class, this::onNPCInteract);
}
private void onNPCInteract(NPCInteractEvent event) {
String npcId = event.getNpcId();
UUID playerId = event.getPlayerId();
// Handle interaction...
dialogueService.startDialogue(playerId, npcId);
}
}
┌─────────────────────────────────────────────────────────────────┐
│ YOUR MOD │
│ NPCDefinition npc = NPCDefinitionBuilder.create("blacksmith") │
│ .displayName("Thorin") │
│ .spawn(NPCSpawnConfig.at("world", 100, 64, 200)) │
│ .build(); │
│ npcManager.spawn(npc); │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ NPC FRAMEWORK │
│ npcManager.spawn(npc) calls: │
│ entityAccessor.spawnEntity(appearance.getEntityType(), loc)│
│ entityAccessor.setMetadata(entityId, "npc_template", npcId)│
│ eventAccessor.dispatch(new NPCSpawnEvent(npcId, entityId)) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ HYTALE ADAPTER │
│ HytaleEntityAccessor.spawnEntity(): │
│ Entity entity = server.getWorld(world) │
│ .spawn(EntityType.get(entityType), x, y, z); │
│ return entity.getId().toString(); │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ HYTALE ENGINE │
│ Native entity created in game world │
└─────────────────────────────────────────────────────────────────┘
The adapter maintains a mapping between NPC template IDs and Hytale entity IDs:
// NPC Manager maintains mappings
public class NPCManager {
private final Map<String, String> npcToEntity = new ConcurrentHashMap<>();
private final Map<String, String> entityToNpc = new ConcurrentHashMap<>();
public void spawn(NPCDefinition definition) {
String entityId = entityAccessor.spawnEntity(
definition.getAppearance().get().getEntityType(),
definition.getSpawnConfig().get().toLocationData()
);
npcToEntity.put(definition.getTemplateId(), entityId);
entityToNpc.put(entityId, definition.getTemplateId());
}
public Optional<String> getNpcIdForEntity(String entityId) {
return Optional.ofNullable(entityToNpc.get(entityId));
}
}
When a player kills an entity for a quest:
┌─────────────────────────────────────────────────────────────────┐
│ HYTALE ENGINE │
│ Player kills entity → EntityDeathEvent fired │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ HYTALE ADAPTER │
│ Converts EntityDeathEvent to: │
│ EntityKillEvent(killerId, victimType, location) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ OBJECTIVE FRAMEWORK │
│ KillObjectiveTracker listens for EntityKillEvent │
│ Updates progress for matching quests │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ QUEST FRAMEWORK │
│ QuestManager notified of objective progress │
│ Checks if quest can complete │
│ Dispatches QuestProgressEvent │
└─────────────────────────────────────────────────────────────────┘
Quests are registered through the Quest Framework, which uses the adapter for:
The dialogue system integrates with Hytale through the UI Framework:
┌─────────────────────────────────────────────────────────────────┐
│ NPC FRAMEWORK │
│ DialogueSession session = dialogueService.startDialogue( │
│ playerId, npcId, "greeting"); │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ UI FRAMEWORK │
│ DialogueUI.show(player, currentNode, options) │
│ Creates HyUIML document for dialogue box │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ HYTALE ADAPTER │
│ HyUIAdapter sends HyUIML to player's client │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ HYTALE CLIENT │
│ Renders dialogue UI with options │
│ Player clicks option → UI callback fired │
└─────────────────────────────────────────────────────────────────┘
// Create dialogue tree (platform-agnostic)
DialogueTree blacksmithDialogue = DialogueTree.builder("blacksmith_main")
.npcId("blacksmith_001")
.displayName("Ironforge Blacksmith")
.startNode("greeting")
.node(DialogueNode.builder("greeting")
.speaker("Thordak")
.text("Welcome to Ironforge! Need something forged?")
.option(DialogueOption.simple("shop", "Show me your wares", "shop_intro"))
.option(DialogueOption.builder("quest")
.text("I heard you need help...")
.targetNode("quest_offer")
.condition(ctx -> !ctx.hasCompletedQuest("blacksmith_quest"))
.build())
.option(DialogueOption.goodbye("Farewell"))
.build())
.build();
// Register dialogue
dialogueRegistry.register(blacksmithDialogue);
eventAccessor.register(DialogueOptionSelectedEvent.class, event -> {
DialogueSession session = sessionManager.getSession(event.getPlayerId());
DialogueOption selected = session.getOption(event.getOptionId());
// Execute option action if any
selected.getAction().ifPresent(action -> action.execute(session.getContext()));
// Navigate to next node
if (selected.getTargetNodeId().isPresent()) {
session.navigateTo(selected.getTargetNodeId().get());
dialogueUI.update(session);
} else {
session.end();
}
});
Before committing code in frameworks/mods:
import hytale.* or import com.hypixel.hytale.* statementsCause: Adapter not loaded or not initialized before your mod.
Solution: Ensure hytale-adapter is in your dependencies and loads before your mod:
<dependency>
<groupId>com.argonathsystems.adapter</groupId>
<artifactId>hytale-adapter</artifactId>
<scope>runtime</scope>
</dependency>
Cause: Event type mismatch or registration issue.
Debug Steps:
eventAccessor.setDebugMode(true); // Log all event dispatches
Cause: Entity spawn failed in Hytale.
Debug Steps:
// Get spawn result
SpawnResult result = entityAccessor.spawnEntityWithResult(entityType, location);
if (result.isFailure()) {
logger.error("Spawn failed: " + result.getFailureReason());
}
The Hytale integration follows these key principles:
This architecture protects your code from Hytale API changes while enabling full game integration.