A system-agnostic Virtual Tabletop (VTT) core designed to run "headless" inside game engines.
VTTale is a headless VTT engine that brings tabletop RPG functionality (dice rolling, character management, game systems) into existing game engines like Hytale. Unlike traditional VTTs with their own UI, VTTale runs as a plugin inside the host game, using its existing chat and command systems.
- Platform Agnostic — Designed to work across multiple game engines via Adapters
- Modular Architecture — Extend functionality without modifying core code
- Multi-System Support — Support for D&D 5e, Pathfinder, and other game systems via modules
- Event-Driven — Type-safe event bus for inter-module communication
- Plugin-Friendly — Third-party developers can create their own extensions
VTTale follows architectural patterns similar to FoundryVTT:
| Aspect | FoundryVTT | VTTale |
|---|---|---|
| Language | JavaScript | Java |
| Event System | Hooks.on() / Hooks.call() |
EventBus.subscribe() / EventBus.publish() |
| Module Discovery | JSON manifest + script loading | Java SPI (ServiceLoader) |
| UI | Full web-based UI | Headless (uses host game's UI) |
| Extensibility | Modules & Systems | Modules, Game Systems & Platform Adapters |
VTTale uses a Micro-kernel / Plug-in Based / Hexagonal architecture.
VTTale/
├── api/ # SDK - Interfaces & Data Classes
├── kernel/ # Core - Module Lifecycle & Event Bus
├── module/ # Built-in Features
│ ├── chat/ # Chat messaging module
│ └── diceroll/ # Dice rolling module
├── gamesystem/ # Game System Rulesets
│ └── dnd5e/ # D&D 5th Edition
└── platform/ # Platform Adapters
└── hytale/ # Hytale game engine adapter
The strictly enforced dependency flow is:
platform → api ← kernel
↑
module/gamesystem
| Module | Description | Dependencies |
|---|---|---|
| api | The "Contract" — Interfaces and Data Classes | None |
| kernel | The "Runner" — Manages Module Lifecycle & Event Bus | api |
| module | Core features (chat, dice rolling) | api |
| gamesystem | Specific game systems (D&D 5e, Pathfinder) | api |
| platform | Game engine bridges (Hytale, Minecraft) | api, kernel (runtime), module (runtime) |
- Language: Java 21
- Build System: Gradle (Kotlin DSL, Multi-module)
- Architecture: Micro-kernel / Plug-in Based / Hexagonal
- Distribution: Fat JAR (All-in-one platform plugin)
- Java 21 or higher
- Gradle 8.x (or use the included wrapper)
# Clone the repository
git clone https://github.com/your-username/VTTale.git
cd VTTale
# Build all modules
./gradlew build
# Build the Hytale platform plugin
./gradlew :platform:hytale:shadowJar./gradlew :platform:hytale:runServerThe project is built as a Single Fat JAR for each platform (e.g., VTTale.jar). This JAR encapsulates:
- The VTT Kernel
- The Platform Adapter (Bootstrap)
- All built-in Modules and Game Systems
Native game engine developers can extend VTTale by creating their own plugins:
- Add
apias a compile-time dependency - Implement the
Moduleinterface - Register your module via the
VTTaleAPI at runtime - Interact with the global
EventBusandRegistry
public class MyCustomModule implements Module {
@Override
public String getName() {
return "my-custom-module";
}
@Override
public void onEnable(Kernel kernel) {
// Subscribe to events
kernel.getEventBus().subscribe(CommandExecutedEvent.class, (event, ctx) -> {
// Handle command execution
});
// Register custom commands
kernel.getCommandRegistry().registerCommand(
"mycommand",
CommandOptions.builder()
.description("My custom command")
.build()
);
}
@Override
public void onDisable(Kernel kernel) {
// Cleanup
}
}Communication happens via a typed Event Bus:
// Subscribe to events
bus.subscribe(MyEvent.class, (evt, ctx) -> {
// Handle event
});
// Publish events
bus.publish(new MyEvent(), context);Commands are registered dynamically at runtime:
kernel.getCommandRegistry().registerCommand(
"roll",
CommandOptions.builder()
.description("Roll dice")
.playerOnly(true)
.build()
);Every event travels with an EventContext containing the SenderID (UUID) and SourceAdapter, allowing responses to be routed back to the correct user in the game world.
VTTale/
├── api/
│ └── src/main/java/dev/giopalma/vttale/api/
│ ├── Kernel.java # Core kernel interface
│ ├── KernelProvider.java # SPI provider interface
│ ├── VTTale.java # Main API entry point
│ ├── command/
│ │ ├── CommandOptions.java # Command configuration
│ │ └── CommandRegistry.java # Command registration interface
│ ├── events/
│ │ ├── Event.java # Base event interface
│ │ ├── EventBus.java # Event bus interface
│ │ ├── EventContext.java # Event routing context
│ │ └── ...
│ └── module/
│ ├── Module.java # Module interface
│ └── ModuleRegistry.java # Module registry interface
├── kernel/
│ └── src/main/java/dev/giopalma/vttale/kernel/
│ ├── VTTaleKernel.java # Kernel implementation
│ ├── VTTaleKernelProvider.java
│ ├── command/
│ │ └── SimpleCommandRegistry.java
│ ├── events/
│ │ └── SimpleEventBus.java
│ └── module/
│ └── SimpleModuleRegistry.java
├── module/
│ └── src/main/java/dev/giopalma/vttale/module/
│ ├── chat/
│ │ ├── ChatModule.java
│ │ ├── PlatformBroadcastEvent.java
│ │ └── SendMessageEvent.java
│ └── diceroll/
│ └── DiceRollModule.java
├── gamesystem/
│ └── src/main/java/dev/giopalma/vttale/gamesystem/
│ └── dnd5e/
│ └── DND5EGameSystem.java
└── platform/
└── hytale/
└── src/main/java/dev/giopalma/vttale/platform/hytale/
├── Command.java
├── HytaleAdapter.java
└── VTTaleHytalePlugin.java
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the TBD License.
- FoundryVTT — For architectural inspiration
- Hytale — For the initial platform target