Initial commit

This commit is contained in:
RedSavant
2026-01-21 14:59:11 +01:00
commit 2033df2655
53 changed files with 7791 additions and 0 deletions

27
.gitignore vendored Normal file
View File

@@ -0,0 +1,27 @@
# Gradle
.gradle/
build/
app/build/
# IDE
.idea/
*.iml
.vscode/
*.swp
.classpath
.project
.settings/
# OS
.DS_Store
Thumbs.db
# Hytale
*.jar
!gradle/wrapper/gradle-wrapper.jar
.hytale-downloader-credentials.json
# Setup
.bin/
server/
src-ref/

99
README.md Normal file
View File

@@ -0,0 +1,99 @@
# Example Hytale Plugin
This is an example Hytale server plugin demonstrating the basic structure and setup.
## Project Structure
```
example-mod/
├── app/
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/org/example/
│ │ │ │ └── ExamplePlugin.java
│ │ │ └── resources/
│ │ │ └── plugin.json
│ │ └── test/
│ ├── build.gradle.kts
├── gradle/
├── server/
├── build.gradle.kts
├── settings.gradle.kts
└── gradlew / gradlew.bat
```
## Setup
### Pre-setup
```bash
# Download the hytale-downloader & cfr (DO IT ONCE)
./setup --download
```
### Setup
```bash
# Download the HytaleServer, finally located here ./server/HytaleServer.jar
./setup --setup
```
### Decompile
```bash
# Decompile ./server/HytaleServer.jar to get the source, finally located here ./src-ref
./setup --decompile
```
## Building
```bash
# Build the plugin
./gradlew build
# The JAR will be in app/build/libs/ExamplePlugin-1.0.0.jar
```
## Installation
1. Build the plugin with `./gradlew build`
2. Copy `app/build/libs/ExamplePlugin-1.0.0.jar` to your Hytale server's `mods/` folder
3. Start the server
## Plugin Structure
### manifest.json
The plugin manifest file defines metadata about your plugin:
```json
{
"Group": "org.example",
"Name": "ExamplePlugin",
"Version": "1.0.0",
"Description": "An example Hytale server plugin",
"Authors": [
{
"Name": "Your Name"
}
],
"Main": "org.example.ExamplePlugin",
"ServerVersion": "*",
"Dependencies": {},
"OptionalDependencies": {},
"DisabledByDefault": false,
"IncludesAssetPack": false,
"SubPlugins": []
}
```
### Main Plugin Class
Your plugin class extends `JavaPlugin` and implements lifecycle methods:
- `setup()` - Called during server setup, register configs here
- `start()` - Called when plugin starts, register commands/events here
- `shutdown()` - Called when plugin stops, cleanup resources here
## Requirements
- Java 25 or later

29
app/build.gradle.kts Normal file
View File

@@ -0,0 +1,29 @@
plugins {
java
}
group = "org.example"
version = "1.0.0"
repositories {
mavenCentral()
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(25)
}
}
dependencies {
// Hytale Server API - compile only since it's provided at runtime
compileOnly(files("../server/Server/HytaleServer.jar"))
// Testing
testImplementation(libs.junit)
}
tasks.jar {
// Set the archive name
archiveBaseName.set("ExamplePlugin")
}

View File

@@ -0,0 +1,33 @@
package org.example;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.util.Config;
/**
* Main example plugin.
* Individual documentation examples are in org.example.docs package.
*/
public class ExamplePlugin extends JavaPlugin {
private Config<MyConfig> config;
public ExamplePlugin(JavaPluginInit init) {
super(init);
config = withConfig(MyConfig.CODEC);
}
@Override
public void setup() {
}
@Override
public void start() {
// Register commands from documentation examples
getCommandRegistry().registerCommand(new org.example.docs.commands.CreatingCommandsExample.HelloCommand());
}
@Override
public void shutdown() {
}
}

View File

@@ -0,0 +1,31 @@
package org.example;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
public class MyConfig {
private String serverName;
private int maxPlayers = 20; // Default value
private boolean debugMode = false;
public static final BuilderCodec<MyConfig> CODEC = BuilderCodec.builder(MyConfig.class, MyConfig::new)
.append(new KeyedCodec<>("ServerName", Codec.STRING),
(config, val) -> config.serverName = val,
config -> config.serverName)
.add()
.append(new KeyedCodec<>("MaxPlayers", Codec.INTEGER),
(config, val) -> config.maxPlayers = val,
config -> config.maxPlayers)
.add()
.append(new KeyedCodec<>("DebugMode", Codec.BOOLEAN),
(config, val) -> config.debugMode = val,
config -> config.debugMode)
.add()
.build();
// Getters...
public String getServerName() { return serverName; }
public int getMaxPlayers() { return maxPlayers; }
public boolean isDebugMode() { return debugMode; }
}

View File

@@ -0,0 +1,595 @@
package org.example.docs.commands;
// Example from: hytale-docs/content/commands/argument-types.en.md
// This file tests that ALL argument-types documentation examples compile correctly
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg;
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect;
import com.hypixel.hytale.server.core.asset.type.particle.config.ParticleSystem;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.protocol.GameMode;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.core.command.system.arguments.types.RelativeDoublePosition;
import com.hypixel.hytale.server.core.command.system.arguments.types.RelativeIntPosition;
import com.hypixel.hytale.server.core.command.system.arguments.types.Coord;
import java.util.UUID;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* Tests argument-types.en.md documentation
*
* IMPORTANT CORRECTIONS FOUND:
* - Documentation uses Component.text() which doesn't exist - use Message.raw() instead
* - Documentation uses playerRef.getPlayer() which doesn't exist - use getReference() for ECS
* - Documentation uses playerRef.getWorld() which doesn't exist - use getWorldUuid()
* - Documentation should use context.sendMessage(Message) instead of context.sender().sendMessage()
*/
public class ArgumentTypesExample {
// ============================================
// From "Primitive Types" section - Examples tab
// ============================================
public static class MathCommand extends AbstractCommand {
private final RequiredArg<Integer> countArg;
private final RequiredArg<Double> multiplierArg;
private final RequiredArg<Boolean> verboseArg;
private final OptionalArg<String> labelArg;
public MathCommand() {
super("math", "Perform mathematical operations");
countArg = withRequiredArg("count", "Number of iterations", ArgTypes.INTEGER);
multiplierArg = withRequiredArg("multiplier", "Multiplication factor", ArgTypes.DOUBLE);
verboseArg = withRequiredArg("verbose", "Show detailed output", ArgTypes.BOOLEAN);
labelArg = withOptionalArg("label", "Optional label", ArgTypes.STRING);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
int count = context.get(countArg);
double mult = context.get(multiplierArg);
boolean verbose = context.get(verboseArg);
String label = context.get(labelArg); // null if not provided
double result = count * mult;
if (verbose) {
String output = (label != null ? label + ": " : "") +
count + " × " + mult + " = " + result;
// CORRECTION: Use context.sendMessage(Message.raw()) instead of context.sender().sendMessage()
context.sendMessage(Message.raw(output));
}
return null;
}
}
// ============================================
// From "Player and Entity Types" - PlayerRef tab
// CORRECTION: playerRef.getPlayer() doesn't exist
// ============================================
public static class TeleportCommand extends AbstractCommand {
private final RequiredArg<PlayerRef> targetArg;
private final OptionalArg<PlayerRef> destinationArg;
public TeleportCommand() {
super("tp", "Teleport players");
targetArg = withRequiredArg("target", "Player to teleport", ArgTypes.PLAYER_REF);
destinationArg = withOptionalArg("destination", "Destination player", ArgTypes.PLAYER_REF);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
PlayerRef targetRef = context.get(targetArg);
// CORRECTION: PlayerRef doesn't have getPlayer() method
// Instead use getReference() to get Ref<EntityStore>
// For now, we use username which IS available
String targetUsername = targetRef.getUsername();
if (context.provided(destinationArg)) {
PlayerRef destRef = context.get(destinationArg);
String destUsername = destRef.getUsername();
context.sendMessage(Message.raw("Would teleport " + targetUsername + " to " + destUsername));
}
return null;
}
}
// ============================================
// From "Player and Entity Types" - Entity tab
// ============================================
public static class EntityInfoCommand extends AbstractCommand {
private final RequiredArg<UUID> entityArg;
public EntityInfoCommand() {
super("entityinfo", "Get entity information");
entityArg = withRequiredArg("entity", "Entity UUID", ArgTypes.UUID);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
UUID entityId = context.get(entityArg);
// Find entity in worlds
for (World world : Universe.get().getWorlds().values()) {
// Search for entity by UUID
// Entity lookup depends on world API
}
context.sendMessage(Message.raw("Entity lookup complete"));
return null;
}
}
// ============================================
// From "Player and Entity Types" - UUID tab
// ============================================
public static class UuidCommand extends AbstractCommand {
private final RequiredArg<UUID> uuidArg;
public UuidCommand() {
super("uuid", "Parse and display UUID");
uuidArg = withRequiredArg("uuid", "UUID to parse", ArgTypes.UUID);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
UUID uuid = context.get(uuidArg);
context.sendMessage(Message.raw(
"UUID: " + uuid.toString() + "\nVersion: " + uuid.version()
));
return null;
}
}
// ============================================
// From "World Types" section
// CORRECTION: Uses Component.text() which doesn't exist
// ============================================
public static class WorldTeleportCommand extends AbstractCommand {
private final RequiredArg<World> worldArg;
private final OptionalArg<RelativeDoublePosition> positionArg;
public WorldTeleportCommand() {
super("wtp", "Teleport to another world");
worldArg = withRequiredArg("world", "Destination world", ArgTypes.WORLD);
positionArg = withOptionalArg("position", "Spawn position", ArgTypes.RELATIVE_POSITION);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
// CORRECTION: Use context.sender() instanceof Player, then cast
if (!(context.sender() instanceof Player player)) {
// CORRECTION: Use Message.raw() not Component.text()
context.sendMessage(Message.raw("Only players can use this command!"));
return null;
}
World targetWorld = context.get(worldArg);
// Note: World.getSpawnPosition() and player.teleport() would need verification
// For now just show the concept works
context.sendMessage(Message.raw("Would teleport to world: " + targetWorld.getName()));
return null;
}
}
// ============================================
// From "Asset Types" - Items tab
// CORRECTION: Uses Component.text() and ItemStack.of() which may not exist
// ============================================
public static class GiveCommand extends AbstractCommand {
private final RequiredArg<Item> itemArg;
private final OptionalArg<Integer> amountArg;
private final OptionalArg<PlayerRef> targetArg;
public GiveCommand() {
super("give", "Give items to players");
itemArg = withRequiredArg("item", "Item to give", ArgTypes.ITEM_ASSET);
amountArg = withOptionalArg("amount", "Stack size", ArgTypes.INTEGER);
targetArg = withOptionalArg("player", "Target player", ArgTypes.PLAYER_REF);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
Item item = context.get(itemArg);
int amount = context.provided(amountArg) ? context.get(amountArg) : 1;
// CORRECTION: playerRef.getPlayer() doesn't exist
// We can only get username from PlayerRef
String targetName;
if (context.provided(targetArg)) {
targetName = context.get(targetArg).getUsername();
} else if (context.sender() instanceof Player p) {
targetName = p.getDisplayName();
} else {
context.sendMessage(Message.raw("Specify a player!"));
return null;
}
// CORRECTION: Use Message.raw() instead of Component.text()
context.sendMessage(
Message.raw("Gave " + amount + "x " + item.getId() + " to " + targetName)
);
return null;
}
}
// ============================================
// From "Asset Types" - Blocks tab
// ============================================
public static class SetBlockCommand extends AbstractCommand {
private final RequiredArg<BlockType> blockArg;
private final RequiredArg<RelativeIntPosition> positionArg;
public SetBlockCommand() {
super("setblock", "Set a block at position");
blockArg = withRequiredArg("block", "Block type", ArgTypes.BLOCK_TYPE_ASSET);
positionArg = withRequiredArg("position", "Target position", ArgTypes.RELATIVE_BLOCK_POSITION);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
if (!(context.sender() instanceof Player player)) {
return null;
}
BlockType blockType = context.get(blockArg);
// Note: player.getBlockPosition() would need verification
// RelativeIntPosition.resolve() takes a Vector3i
context.sendMessage(
Message.raw("Set block to " + blockType.getId())
);
return null;
}
}
// ============================================
// From "Asset Types" - Effects tab
// ============================================
public static class EffectCommand extends AbstractCommand {
private final RequiredArg<EntityEffect> effectArg;
private final OptionalArg<Integer> durationArg;
private final OptionalArg<Integer> amplifierArg;
public EffectCommand() {
super("effect", "Apply effect to player");
effectArg = withRequiredArg("effect", "Effect to apply", ArgTypes.EFFECT_ASSET);
durationArg = withOptionalArg("duration", "Duration in ticks", ArgTypes.INTEGER);
amplifierArg = withOptionalArg("amplifier", "Effect level", ArgTypes.INTEGER);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
if (!(context.sender() instanceof Player player)) {
return null;
}
EntityEffect effect = context.get(effectArg);
int duration = context.provided(durationArg) ? context.get(durationArg) : 600;
int amplifier = context.provided(amplifierArg) ? context.get(amplifierArg) : 0;
// Note: player.addEffect() would need verification
context.sendMessage(Message.raw("Applied effect: " + effect.getId()));
return null;
}
}
// ============================================
// From "Asset Types" - Particles tab
// ============================================
public static class ParticleCommand extends AbstractCommand {
private final RequiredArg<ParticleSystem> particleArg;
private final OptionalArg<RelativeDoublePosition> positionArg;
public ParticleCommand() {
super("particle", "Spawn particle effect");
particleArg = withRequiredArg("particle", "Particle system", ArgTypes.PARTICLE_SYSTEM);
positionArg = withOptionalArg("position", "Spawn position", ArgTypes.RELATIVE_POSITION);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
if (!(context.sender() instanceof Player player)) {
return null;
}
ParticleSystem particle = context.get(particleArg);
// Note: Would need to verify World.spawnParticle() exists
context.sendMessage(Message.raw("Spawned particle: " + particle.getId()));
return null;
}
}
// ============================================
// From "Position Types" - 3D Positions tab
// ============================================
public static class TeleportPosCommand extends AbstractCommand {
private final RequiredArg<RelativeDoublePosition> posArg;
public TeleportPosCommand() {
super("tppos", "Teleport to coordinates");
posArg = withRequiredArg("position", "Target position", ArgTypes.RELATIVE_POSITION);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
if (!(context.sender() instanceof Player player)) {
return null;
}
RelativeDoublePosition relPos = context.get(posArg);
// Note: Need to verify player.getPosition() and relPos.resolve() APIs
// player.teleport(absolutePos) would also need verification
context.sendMessage(Message.raw("Teleported to position"));
return null;
}
}
// ============================================
// From "Position Types" - Block Positions tab
// ============================================
public static class FillCommand extends AbstractCommand {
private final RequiredArg<RelativeIntPosition> pos1Arg;
private final RequiredArg<RelativeIntPosition> pos2Arg;
private final RequiredArg<BlockType> blockArg;
public FillCommand() {
super("fill", "Fill region with blocks");
pos1Arg = withRequiredArg("from", "First corner", ArgTypes.RELATIVE_BLOCK_POSITION);
pos2Arg = withRequiredArg("to", "Second corner", ArgTypes.RELATIVE_BLOCK_POSITION);
blockArg = withRequiredArg("block", "Block type", ArgTypes.BLOCK_TYPE_ASSET);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
if (!(context.sender() instanceof Player player)) {
return null;
}
// RelativeIntPosition values
RelativeIntPosition from = context.get(pos1Arg);
RelativeIntPosition to = context.get(pos2Arg);
BlockType block = context.get(blockArg);
// Note: Need to verify these APIs exist
// player.getBlockPosition() returns Vector3i?
// world.setBlock(Vector3i, BlockType)?
context.sendMessage(Message.raw("Filled region with " + block.getId()));
return null;
}
}
// ============================================
// From "Position Types" - Single Coords tab
// ============================================
public static class SetYCommand extends AbstractCommand {
private final RequiredArg<Coord> yArg;
public SetYCommand() {
super("sety", "Teleport to Y level");
yArg = withRequiredArg("y", "Y coordinate", ArgTypes.RELATIVE_DOUBLE_COORD);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
if (!(context.sender() instanceof Player player)) {
return null;
}
Coord relY = context.get(yArg);
// Note: relY.resolve(double) API needs verification
context.sendMessage(Message.raw("Set Y level"));
return null;
}
}
// ============================================
// From "Vector Types" - Examples tab
// ============================================
public static class LookCommand extends AbstractCommand {
private final RequiredArg<Vector3f> rotationArg;
public LookCommand() {
super("look", "Set player rotation");
rotationArg = withRequiredArg("rotation", "Pitch Yaw Roll", ArgTypes.ROTATION);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
if (!(context.sender() instanceof Player player)) {
return null;
}
Vector3f rotation = context.get(rotationArg);
// rotation.x = pitch, rotation.y = yaw, rotation.z = roll
// Note: player.setRotation() API needs verification
// CORRECTION: Vector3f uses public fields, not methods
context.sendMessage(Message.raw(
"Rotation set to Pitch: " + rotation.x +
", Yaw: " + rotation.y +
", Roll: " + rotation.z
));
return null;
}
}
// ============================================
// From "Special Types" - Color tab
// ============================================
public static class ColorCommand extends AbstractCommand {
private final RequiredArg<Integer> colorArg;
public ColorCommand() {
super("color", "Set display color");
colorArg = withRequiredArg("color", "Color value", ArgTypes.COLOR);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
int color = context.get(colorArg);
int alpha = (color >> 24) & 0xFF;
int red = (color >> 16) & 0xFF;
int green = (color >> 8) & 0xFF;
int blue = color & 0xFF;
context.sendMessage(
Message.raw("Color: ARGB(" + alpha + ", " + red + ", " + green + ", " + blue + ")")
);
return null;
}
}
// ============================================
// From "Special Types" - Game Mode tab
// ============================================
public static class GameModeCommand extends AbstractCommand {
private final RequiredArg<GameMode> modeArg;
private final OptionalArg<PlayerRef> playerArg;
public GameModeCommand() {
super("gamemode", "Change game mode");
modeArg = withRequiredArg("mode", "Game mode", ArgTypes.GAME_MODE);
playerArg = withOptionalArg("player", "Target player", ArgTypes.PLAYER_REF);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
GameMode mode = context.get(modeArg);
String targetName;
if (context.provided(playerArg)) {
PlayerRef ref = context.get(playerArg);
targetName = ref.getUsername();
} else if (context.sender() instanceof Player p) {
targetName = p.getDisplayName();
} else {
context.sendMessage(Message.raw("Specify a player!"));
return null;
}
// Set game mode using ChangeGameModeEvent or component
context.sendMessage(Message.raw("Game mode changed to " + mode.name() + " for " + targetName));
return null;
}
}
// ============================================
// From "Special Types" - Tick Rate tab
// ============================================
public static class TickRateCommand extends AbstractCommand {
private final RequiredArg<Integer> rateArg;
public TickRateCommand() {
super("tickrate", "Set world tick rate");
rateArg = withRequiredArg("rate", "Tick rate", ArgTypes.TICK_RATE);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
if (!(context.sender() instanceof Player player)) {
return null;
}
int tps = context.get(rateArg); // Returns Integer (TPS)
// Set tick rate on world...
context.sendMessage(Message.raw("Tick rate set to " + tps + " TPS"));
return null;
}
}
// ============================================
// From "Custom Argument Types" - Enum Arguments section
// ============================================
public enum Difficulty {
EASY, NORMAL, HARD, EXTREME
}
public static class DifficultyCommand extends AbstractCommand {
private final RequiredArg<Difficulty> difficultyArg;
public DifficultyCommand() {
super("difficulty", "Set server difficulty");
difficultyArg = withRequiredArg(
"level",
"Difficulty level",
ArgTypes.forEnum("Difficulty", Difficulty.class)
);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
Difficulty level = context.get(difficultyArg);
// Apply difficulty setting
context.sendMessage(
Message.raw("Difficulty set to " + level.name())
);
return null;
}
}
// ============================================
// From "Custom Argument Types" - List Arguments section
// ============================================
public static class MultiTeleportCommand extends AbstractCommand {
private final RequiredArg<List<PlayerRef>> playersArg;
private final RequiredArg<RelativeDoublePosition> positionArg;
public MultiTeleportCommand() {
super("multitp", "Teleport multiple players");
playersArg = withListRequiredArg("players", "Target players", ArgTypes.PLAYER_REF);
positionArg = withRequiredArg("position", "Destination", ArgTypes.RELATIVE_POSITION);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
List<PlayerRef> playerRefs = context.get(playersArg);
RelativeDoublePosition relPos = context.get(positionArg);
// Note: playerRef.getPlayer() doesn't exist, use getReference()
for (PlayerRef ref : playerRefs) {
// Would need to use ECS to teleport
String username = ref.getUsername();
}
context.sendMessage(
Message.raw("Teleported " + playerRefs.size() + " players")
);
return null;
}
}
// Note: Argument Validation section - .validate() method usage would need verification
}

View File

@@ -0,0 +1,399 @@
package org.example.docs.commands;
// Example from: hytale-docs/content/commands/command-context.en.md
// This file tests that ALL command-context documentation examples compile correctly
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.command.system.CommandSender;
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.DefaultArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg;
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
import com.hypixel.hytale.server.core.command.system.exceptions.GeneralCommandException;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import java.util.concurrent.CompletableFuture;
/**
* Tests command-context.en.md documentation
*
* IMPORTANT CORRECTIONS FOUND:
* - Line 46: context.sender().sendMessage("text") should use Message.raw()
* - Line 189: playerRef.getWorld() doesn't exist - use getWorldUuid()
* - Line 201: playerRef.sendMessage() exists, but Message.of() doesn't - use Message.raw()
*/
public class CommandContextExample {
// ============================================
// From "Accessing the Context" section
// ============================================
public static class BasicContextCommand extends AbstractCommand {
public BasicContextCommand() {
super("basiccontext", "Shows basic context usage");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
// Use context here
return null;
}
}
// ============================================
// From "Command Sender" section
// ============================================
public static class SenderCommand extends AbstractCommand {
public SenderCommand() {
super("sender", "Demonstrates sender access");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
CommandSender sender = context.sender();
// Send messages (must use Message class)
sender.sendMessage(Message.raw("Hello!"));
sender.sendMessage(Message.translation("my.translation.key"));
// Check permissions
if (sender.hasPermission("myplugin.admin")) {
// ...
}
return null;
}
}
// ============================================
// From "Checking Sender Type" section
// CORRECTION: sendMessage needs Message.raw()
// ============================================
public static class SenderTypeCommand extends AbstractCommand {
public SenderTypeCommand() {
super("sendertype", "Checks sender type");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
if (context.sender() instanceof Player) {
Player player = (Player) context.sender();
// Player-specific logic
} else {
// Console or other sender
// CORRECTION: Use Message.raw() instead of raw String
context.sender().sendMessage(Message.raw("This command requires a player!"));
}
return null;
}
}
// ============================================
// From "Getting Arguments" - Required Arguments
// ============================================
public static class RequiredArgsCommand extends AbstractCommand {
private final RequiredArg<PlayerRef> playerArg;
private final RequiredArg<Integer> countArg;
public RequiredArgsCommand() {
super("requiredargs", "Required arguments example");
playerArg = withRequiredArg("player", "Target player", ArgTypes.PLAYER_REF);
countArg = withRequiredArg("count", "Count value", ArgTypes.INTEGER);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
// In execute():
PlayerRef player = context.get(playerArg); // Never null for required args
int count = context.get(countArg); // Never null for required args
return null;
}
}
// ============================================
// From "Getting Arguments" - Optional Arguments
// ============================================
public static class OptionalArgsCommand extends AbstractCommand {
private final OptionalArg<String> reasonArg;
public OptionalArgsCommand() {
super("optionalargs", "Optional arguments example");
reasonArg = withOptionalArg("reason", "Optional reason", ArgTypes.STRING);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
// Get value (may be null if not provided)
String reason = context.get(reasonArg);
// Check if provided before using
if (context.provided(reasonArg)) {
String providedReason = context.get(reasonArg);
}
return null;
}
}
// ============================================
// From "Getting Arguments" - Default Arguments
// ============================================
public static class DefaultArgsCommand extends AbstractCommand {
private final DefaultArg<Integer> countArg; // Default: 1
public DefaultArgsCommand() {
super("defaultargs", "Default arguments example");
countArg = withDefaultArg("count", "Number", ArgTypes.INTEGER, 1, "1");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
// Always returns a value (never null)
int count = context.get(countArg); // Returns default if not specified
return null;
}
}
// ============================================
// From "Getting Arguments" - Flag Arguments
// ============================================
public static class FlagArgsCommand extends AbstractCommand {
private final FlagArg silentFlag;
public FlagArgsCommand() {
super("flagargs", "Flag arguments example");
silentFlag = withFlagArg("silent", "Silent mode");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
// Check if flag was provided
boolean isSilent = context.provided(silentFlag);
return null;
}
}
// ============================================
// From "Input String" section
// ============================================
public static class InputStringCommand extends AbstractCommand {
public InputStringCommand() {
super("inputstring", "Shows input string access");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
String input = context.getInputString();
// For "/give player123 sword 5" -> "give player123 sword 5"
return null;
}
}
// ============================================
// From "The Command" section
// ============================================
public static class CommandInfoCommand extends AbstractCommand {
public CommandInfoCommand() {
super("commandinfo", "Shows command info access");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
AbstractCommand command = context.getCalledCommand();
String commandName = command.getName();
String fullName = command.getFullyQualifiedName(); // e.g., "admin kick"
return null;
}
}
// ============================================
// From "PLAYER_REF Fallback" section
// CORRECTION: PlayerRef doesn't have getPlayer() or getWorld()
// ============================================
public static class PlayerRefFallbackCommand extends AbstractCommand {
private final OptionalArg<PlayerRef> targetArg;
public PlayerRefFallbackCommand() {
super("playerreffallback", "PlayerRef fallback example");
targetArg = withOptionalArg("target", "Target player", ArgTypes.PLAYER_REF);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
PlayerRef target = context.get(targetArg);
// Manual fallback if null
// CORRECTION: Use getPlayerRef() from Player (though deprecated)
if (target == null && context.sender() instanceof Player player) {
target = player.getPlayerRef(); // Note: getPlayerRef() is deprecated
}
// ...
return null;
}
}
// ============================================
// From "World Argument Fallback" section
// ============================================
public static class WorldFallbackCommand extends AbstractCommand {
private final OptionalArg<World> worldArg;
public WorldFallbackCommand() {
super("worldfallback", "World fallback example");
worldArg = withOptionalArg("world", "Target world", ArgTypes.WORLD);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
World world = context.get(worldArg);
// Manual fallback if null
if (world == null && context.sender() instanceof Player player) {
world = player.getWorld(); // Note: Need to verify this exists
}
// ...
return null;
}
}
// ============================================
// From "Error Handling" section
// CORRECTION: PlayerRef doesn't have getPlayer()
// ============================================
public static class ErrorHandlingCommand extends AbstractCommand {
private final RequiredArg<PlayerRef> playerArg;
public ErrorHandlingCommand() {
super("errorhandling", "Error handling example");
playerArg = withRequiredArg("player", "Target player", ArgTypes.PLAYER_REF);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
PlayerRef playerRef = context.get(playerArg);
// CORRECTION: PlayerRef doesn't have getPlayer() - use ECS pattern
// Instead, we can check if the reference is valid
Ref<EntityStore> ref = playerRef.getReference();
if (ref == null || !ref.isValid()) {
throw new GeneralCommandException(
Message.translation("error.player.offline")
.param("player", playerRef.getUsername())
);
}
// Continue execution
return null;
}
}
// ============================================
// From "Asynchronous Commands" section
// CORRECTION: playerRef.getWorld() doesn't exist
// ============================================
public static class AsyncCommand extends AbstractCommand {
public AsyncCommand() {
super("asynccmd", "Async command example");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
if (!(context.sender() instanceof Player player)) {
return null;
}
PlayerRef playerRef = player.getPlayerRef(); // Note: deprecated
// CORRECTION: Use getWorldUuid() instead of getWorld()
// Or use player.getWorld() directly
World world = player.getWorld();
return CompletableFuture.runAsync(() -> {
// Async operation (e.g., database query)
// PlayerData data = database.loadData(playerRef.getUuid());
// Return to world thread for game logic
world.execute(() -> {
Ref<EntityStore> ref = playerRef.getReference();
if (ref != null) {
// applyData(ref, data);
playerRef.sendMessage(Message.raw("Data loaded!"));
}
});
});
}
}
// ============================================
// From "Complete Example" section
// CORRECTION: playerRef.getPlayer() doesn't exist
// ============================================
public static class GiveCommand extends AbstractCommand {
private final RequiredArg<Item> itemArg;
private final OptionalArg<PlayerRef> targetArg;
private final DefaultArg<Integer> countArg;
private final FlagArg silentFlag;
public GiveCommand() {
super("give", "Give items to a player");
itemArg = withRequiredArg("item", "Item to give", ArgTypes.ITEM_ASSET);
targetArg = withOptionalArg("target", "Target player", ArgTypes.PLAYER_REF);
countArg = withDefaultArg("count", "Amount", ArgTypes.INTEGER, 1, "1");
silentFlag = withFlagArg("silent", "Don't broadcast");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
// Get arguments
Item item = context.get(itemArg);
int count = context.get(countArg); // Uses default if not specified
boolean silent = context.provided(silentFlag);
// Get target with fallback to sender
PlayerRef targetRef = context.get(targetArg);
if (targetRef == null && context.sender() instanceof Player senderPlayer) {
targetRef = senderPlayer.getPlayerRef(); // Note: deprecated
}
if (targetRef == null) {
throw new GeneralCommandException(
Message.raw("Must specify a target player!")
);
}
// Validate target is online
// CORRECTION: PlayerRef doesn't have getPlayer()
// Use getReference() to check if valid
Ref<EntityStore> ref = targetRef.getReference();
if (ref == null || !ref.isValid()) {
throw new GeneralCommandException(
Message.raw("Player is not online!")
);
}
// Execute - give item to player
// ... (would use ECS/inventory APIs)
// Feedback
if (!silent) {
context.sender().sendMessage(Message.raw(
"Gave " + count + "x " + item.getId() + " to " + targetRef.getUsername()
));
}
return null;
}
}
}

View File

@@ -0,0 +1,162 @@
package org.example.docs.commands;
// Example from: hytale-docs/content/commands/creating-commands.en.md
// This file tests that ALL commands documentation examples compile correctly
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.DefaultArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg;
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
// For ECS operations: import com.hypixel.hytale.component.Ref;
// For ECS operations: import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
import com.hypixel.hytale.server.core.Message;
import java.util.concurrent.CompletableFuture;
/**
* Tests creating-commands.en.md documentation
*/
public class CreatingCommandsExample {
// From "Basic Command Structure" section
public static class HelloCommand extends AbstractCommand {
public HelloCommand() {
super("hello", "Sends a greeting message");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
context.sendMessage(Message.raw("Hello, World!"));
return null;
}
}
// From "Command with Arguments" section
public static class GiveCommand extends AbstractCommand {
private final RequiredArg<PlayerRef> targetArg;
private final RequiredArg<Item> itemArg;
public GiveCommand() {
super("give", "Give an item to a player");
targetArg = withRequiredArg("player", "Target player", ArgTypes.PLAYER_REF);
itemArg = withRequiredArg("item", "Item to give", ArgTypes.ITEM_ASSET);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
PlayerRef targetRef = context.get(targetArg);
Item item = context.get(itemArg);
// PlayerRef provides direct access to player info
String username = targetRef.getUsername();
// For ECS operations, use getReference() to access the EntityStore
// Ref<EntityStore> entityRef = targetRef.getReference();
// Give item to player...
context.sendMessage(Message.raw("Gave item to " + username));
return null;
}
}
// From "Adding Aliases" section
public static class TeleportCommand extends AbstractCommand {
public TeleportCommand() {
super("teleport", "Teleport to a location");
addAliases("tp", "warp");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
return null;
}
}
// From "Optional Arguments" section
public static class GiveWithOptionalCommand extends AbstractCommand {
private final RequiredArg<PlayerRef> targetArg;
private final RequiredArg<Item> itemArg;
private final OptionalArg<Integer> countArg;
public GiveWithOptionalCommand() {
super("giveopt", "Give items to a player");
targetArg = withRequiredArg("player", "Target player", ArgTypes.PLAYER_REF);
itemArg = withRequiredArg("item", "Item to give", ArgTypes.ITEM_ASSET);
countArg = withOptionalArg("count", "Number of items", ArgTypes.INTEGER);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
PlayerRef targetRef = context.get(targetArg);
Item item = context.get(itemArg);
Integer count = context.get(countArg); // May be null
int amount = count != null ? count : 1;
// Give items to player...
return null;
}
}
// From "Default Arguments" section
public static class GiveWithDefaultCommand extends AbstractCommand {
private final DefaultArg<Integer> countArg;
public GiveWithDefaultCommand() {
super("givedef", "Give items to a player");
countArg = withDefaultArg("count", "Number of items",
ArgTypes.INTEGER, 1, "defaults to 1");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
int count = context.get(countArg); // Never null, uses default
return null;
}
}
// From "Flag Arguments" section
public static class BroadcastCommand extends AbstractCommand {
private final FlagArg silentFlag;
public BroadcastCommand() {
super("broadcast", "Send a message to all players");
silentFlag = withFlagArg("silent", "Don't show sender name");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
boolean silent = context.provided(silentFlag);
return null;
}
}
// From "Requiring Confirmation" section
public static class ResetCommand extends AbstractCommand {
public ResetCommand() {
super("reset", "Reset all player data", true); // requiresConfirmation = true
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
// This only runs if --confirm was provided
return null;
}
}
}

View File

@@ -0,0 +1,331 @@
package org.example.docs.commands;
// Example from: hytale-docs/content/commands/subcommands.en.md
// This file tests that ALL subcommands documentation examples compile correctly
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection;
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg;
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import java.util.concurrent.CompletableFuture;
/**
* Tests subcommands.en.md documentation
*
* IMPORTANT CORRECTIONS FOUND:
* - playerRef.getPlayer() doesn't exist - PlayerRef only provides username, UUID, and getReference()
* - Player.kick(String) may not exist with that signature - needs verification
*/
public class SubcommandsExample {
// ============================================
// From "Creating Subcommands" - Basic Structure
// ============================================
public static class AdminCommand extends AbstractCommand {
public AdminCommand() {
super("admin", "Administration commands");
// Add subcommands
addSubCommand(new KickSubCommand());
addSubCommand(new BanSubCommand());
addSubCommand(new MuteSubCommand());
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
// This runs when no subcommand is specified
context.sender().sendMessage(Message.raw("Usage: /admin <kick|ban|mute>"));
return null;
}
}
// ============================================
// From "Subcommand Implementation"
// CORRECTION: playerRef.getPlayer() doesn't exist
// ============================================
public static class KickSubCommand extends AbstractCommand {
private final RequiredArg<PlayerRef> playerArg;
private final OptionalArg<String> reasonArg;
public KickSubCommand() {
super("kick", "Kick a player from the server");
playerArg = withRequiredArg("player", "Player to kick", ArgTypes.PLAYER_REF);
reasonArg = withOptionalArg("reason", "Kick reason", ArgTypes.STRING);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
PlayerRef target = context.get(playerArg);
String reason = context.get(reasonArg);
// CORRECTION: PlayerRef doesn't have getPlayer() method
// We can only verify the player is online via getReference()
Ref<EntityStore> ref = target.getReference();
if (ref != null && ref.isValid()) {
// To kick, would need to access the PacketHandler or use events
// target.getPacketHandler().disconnect(reason);
String kickReason = reason != null ? reason : "Kicked by admin";
context.sender().sendMessage(Message.raw("Kicked " + target.getUsername() + ": " + kickReason));
} else {
context.sender().sendMessage(Message.raw("Player " + target.getUsername() + " is not online"));
}
return null;
}
}
// Placeholder subcommands for AdminCommand
public static class BanSubCommand extends AbstractCommand {
private final RequiredArg<PlayerRef> playerArg;
public BanSubCommand() {
super("ban", "Ban a player from the server");
playerArg = withRequiredArg("player", "Player to ban", ArgTypes.PLAYER_REF);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
PlayerRef target = context.get(playerArg);
context.sender().sendMessage(Message.raw("Banned " + target.getUsername()));
return null;
}
}
public static class MuteSubCommand extends AbstractCommand {
private final RequiredArg<PlayerRef> playerArg;
public MuteSubCommand() {
super("mute", "Mute a player");
playerArg = withRequiredArg("player", "Player to mute", ArgTypes.PLAYER_REF);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
PlayerRef target = context.get(playerArg);
context.sender().sendMessage(Message.raw("Muted " + target.getUsername()));
return null;
}
}
// ============================================
// From "Command Collections" section
// ============================================
public static class ManageCommand extends AbstractCommandCollection {
public ManageCommand() {
super("manage", "Management commands");
addSubCommand(new ManageUsersCommand());
addSubCommand(new ManageWorldsCommand());
addSubCommand(new ManagePluginsCommand());
}
}
// ============================================
// From "Nested Subcommands" section
// ============================================
public static class ManageUsersCommand extends AbstractCommand {
public ManageUsersCommand() {
super("users", "User management");
addSubCommand(new ListUsersCommand()); // /manage users list
addSubCommand(new AddUserCommand()); // /manage users add
addSubCommand(new RemoveUserCommand()); // /manage users remove
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
// Show usage for /manage users
context.sender().sendMessage(Message.raw("Usage: /manage users <list|add|remove>"));
return null;
}
}
// Nested subcommand stubs
public static class ListUsersCommand extends AbstractCommand {
public ListUsersCommand() {
super("list", "List all users");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
return null;
}
}
public static class AddUserCommand extends AbstractCommand {
public AddUserCommand() {
super("add", "Add a user");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
return null;
}
}
public static class RemoveUserCommand extends AbstractCommand {
public RemoveUserCommand() {
super("remove", "Remove a user");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
return null;
}
}
// Placeholder manage subcommands
public static class ManageWorldsCommand extends AbstractCommand {
public ManageWorldsCommand() {
super("worlds", "World management");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
return null;
}
}
public static class ManagePluginsCommand extends AbstractCommand {
public ManagePluginsCommand() {
super("plugins", "Plugin management");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
return null;
}
}
// ============================================
// From "Subcommand Aliases" section
// ============================================
public static class TeleportCommand extends AbstractCommand {
public TeleportCommand() {
super("teleport", "Teleport commands");
addAliases("tp");
addSubCommand(new TeleportHereCommand());
addSubCommand(new TeleportAllCommand());
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
return null;
}
}
public static class TeleportHereCommand extends AbstractCommand {
private final RequiredArg<PlayerRef> playerArg;
public TeleportHereCommand() {
super("here", "Teleport player to you");
addAliases("h", "tome");
playerArg = withRequiredArg("player", "Player to teleport", ArgTypes.PLAYER_REF);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
PlayerRef target = context.get(playerArg);
context.sender().sendMessage(Message.raw("Teleporting " + target.getUsername() + " to you"));
return null;
}
}
public static class TeleportAllCommand extends AbstractCommand {
public TeleportAllCommand() {
super("all", "Teleport all players");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
return null;
}
}
// ============================================
// From "Command Variants" section
// ============================================
public static class TpCommand extends AbstractCommand {
private final RequiredArg<PlayerRef> targetArg;
public TpCommand() {
super("tp", "Teleport command");
// Main variant: /tp <player>
targetArg = withRequiredArg("target", "Player to teleport to", ArgTypes.PLAYER_REF);
// Add variant: /tp <player> <destination>
addUsageVariant(new TpToPlayerVariant());
// Note: Position variant would need different argument types
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
// Teleport sender to target player
PlayerRef target = context.get(targetArg);
context.sender().sendMessage(Message.raw("Teleporting to " + target.getUsername()));
return null;
}
}
// ============================================
// From "Variant Implementation" section
// ============================================
public static class TpToPlayerVariant extends AbstractCommand {
private final RequiredArg<PlayerRef> playerArg;
private final RequiredArg<PlayerRef> destinationArg;
public TpToPlayerVariant() {
// Note: Documentation says "no name for variants - use description only"
// But AbstractCommand requires name, so we pass empty or description
super("Teleport one player to another");
playerArg = withRequiredArg("player", "Player to teleport", ArgTypes.PLAYER_REF);
destinationArg = withRequiredArg("destination", "Destination player", ArgTypes.PLAYER_REF);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
PlayerRef player = context.get(playerArg);
PlayerRef destination = context.get(destinationArg);
// Teleport player to destination
context.sender().sendMessage(Message.raw("Teleporting " + player.getUsername() + " to " + destination.getUsername()));
return null;
}
}
// ============================================
// From "Permission Inheritance" section
// ============================================
public static class KickWithPermissionCommand extends AbstractCommand {
public KickWithPermissionCommand() {
super("kick", "Kick a player");
// Custom permission instead of auto-generated
// Note: requirePermission() method needs verification
// requirePermission("myplugin.admin.kick");
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
return null;
}
}
}

View File

@@ -0,0 +1,68 @@
package org.example.docs.coreconcepts;
// Example from: hytale-docs/content/core-concepts/assets.en.md
// This file tests that ALL assets documentation examples compile correctly
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.logger.HytaleLogger;
import java.util.logging.Level;
/**
* Tests ALL assets.en.md documentation examples
*/
public class AssetsExample {
// ============================================
// From "Working with Items" section (lines 27-37)
// ============================================
public void getItemExample(HytaleLogger logger) {
// Get an item by identifier
Item sword = Item.getAssetMap().getAsset("hytale:iron_sword");
// Check if item exists
if (sword != null) {
logger.at(Level.INFO).log("Found item: " + sword.getId());
}
}
// ============================================
// From "Creating ItemStacks" section (lines 41-54)
// ============================================
public void createItemStackExample() {
// Create an ItemStack with quantity
ItemStack stack = new ItemStack("hytale:iron_sword", 5);
// Or with just the item ID (quantity defaults to 1)
ItemStack singleItem = new ItemStack("hytale:iron_sword");
// Access properties
String itemId = stack.getItemId();
int quantity = stack.getQuantity();
}
// ============================================
// From "Getting a BlockType" section (lines 60-63)
// ============================================
public void getBlockTypeExample() {
BlockType stone = BlockType.getAssetMap().getAsset("hytale:stone");
}
// ============================================
// From "Block in World" section (lines 67-75)
// Note: Requires Player and World context
// ============================================
public void blockInWorldExample(Player player) {
int x = 0, y = 0, z = 0;
// Get block at position
World world = player.getWorld();
BlockType blockType = world.getBlockType(x, y, z);
// Set block at position
world.setBlock(x, y, z, "hytale:stone");
}
}

View File

@@ -0,0 +1,160 @@
package org.example.docs.coreconcepts;
// Example from: hytale-docs/content/core-concepts/codecs.en.md
// This file tests that ALL codecs documentation examples compile correctly
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.codecs.array.ArrayCodec;
import com.hypixel.hytale.codec.codecs.map.ObjectMapCodec;
import com.hypixel.hytale.codec.codecs.EnumCodec;
import com.hypixel.hytale.codec.validation.Validators;
import com.hypixel.hytale.server.core.util.Config;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
/**
* Tests ALL codecs.en.md documentation examples
*/
public class CodecsExample {
// ============================================
// From "Basic Codecs" section - testing all primitive codecs
// ============================================
public void testBasicCodecs() {
Codec<String> stringCodec = Codec.STRING;
Codec<Integer> intCodec = Codec.INTEGER;
Codec<Long> longCodec = Codec.LONG;
Codec<Float> floatCodec = Codec.FLOAT;
Codec<Double> doubleCodec = Codec.DOUBLE;
Codec<Boolean> boolCodec = Codec.BOOLEAN;
Codec<Byte> byteCodec = Codec.BYTE;
Codec<Short> shortCodec = Codec.SHORT;
}
// ============================================
// From "List and Map Codecs" section
// ============================================
public void testCollectionCodecs() {
// Array of strings
Codec<String[]> stringArray = new ArrayCodec<>(Codec.STRING, String[]::new);
// Map with string keys
Codec<Map<String, Integer>> stringIntMap = new ObjectMapCodec<>(
Codec.INTEGER,
LinkedHashMap::new,
key -> key, // Key to string
str -> str // String to key
);
}
// ============================================
// From "Enum Codecs" section
// ============================================
public enum GameMode {
SURVIVAL, CREATIVE, ADVENTURE
}
public void testEnumCodec() {
Codec<GameMode> gameModeCodec = new EnumCodec<>(GameMode.class);
}
// ============================================
// From "Required vs Optional Fields" section
// ============================================
public static class ConfigWithValidator {
private String serverName;
private int maxPlayers = 20;
public static final BuilderCodec<ConfigWithValidator> CODEC = BuilderCodec.builder(ConfigWithValidator.class, ConfigWithValidator::new)
.append(new KeyedCodec<>("ServerName", Codec.STRING),
(config, val) -> config.serverName = val,
config -> config.serverName)
.addValidator(Validators.nonNull()) // Makes field required
.add()
.append(new KeyedCodec<>("MaxPlayers", Codec.INTEGER),
(config, val) -> config.maxPlayers = val,
config -> config.maxPlayers)
.add() // Optional - uses default value from class
.build();
}
// ============================================
// From "Nested Codecs" section
// ============================================
public static class GeneralSettings {
private String name;
public static final BuilderCodec<GeneralSettings> CODEC = BuilderCodec.builder(GeneralSettings.class, GeneralSettings::new)
.append(new KeyedCodec<>("Name", Codec.STRING),
(s, val) -> s.name = val,
s -> s.name)
.add()
.build();
}
public static class DatabaseSettings {
private String url;
public static final BuilderCodec<DatabaseSettings> CODEC = BuilderCodec.builder(DatabaseSettings.class, DatabaseSettings::new)
.append(new KeyedCodec<>("Url", Codec.STRING),
(s, val) -> s.url = val,
s -> s.url)
.add()
.build();
}
public static class ServerSettings {
private GeneralSettings general;
private DatabaseSettings database;
public static final BuilderCodec<ServerSettings> CODEC = BuilderCodec.builder(ServerSettings.class, ServerSettings::new)
.append(new KeyedCodec<>("General", GeneralSettings.CODEC),
(s, val) -> s.general = val,
s -> s.general)
.add()
.append(new KeyedCodec<>("Database", DatabaseSettings.CODEC),
(s, val) -> s.database = val,
s -> s.database)
.add()
.build();
}
// ============================================
// From "Inheritance" section
// ============================================
public static class BaseEntity {
protected String id;
public BaseEntity() {}
public static final BuilderCodec<BaseEntity> BASE_CODEC = BuilderCodec.builder(BaseEntity.class, BaseEntity::new)
.append(new KeyedCodec<>("Id", Codec.STRING),
(e, val) -> e.id = val,
e -> e.id)
.add()
.build();
}
public static class PlayerEntity extends BaseEntity {
private String username;
public PlayerEntity() { super(); }
public static final BuilderCodec<PlayerEntity> CODEC = BuilderCodec.builder(PlayerEntity.class, PlayerEntity::new, BaseEntity.BASE_CODEC)
.append(new KeyedCodec<>("Username", Codec.STRING),
(e, val) -> e.username = val,
e -> e.username)
.add()
.build();
}
// ============================================
// From "Named Configs" section - pattern only (requires plugin context)
// ============================================
// In a plugin class:
// Config<MyConfig> mainConfig = withConfig("main", MyConfig.CODEC);
// Config<DatabaseConfig> dbConfig = withConfig("database", DatabaseConfig.CODEC);
}

View File

@@ -0,0 +1,84 @@
package org.example.docs.coreconcepts;
// Example from: hytale-docs/content/core-concepts/registries.en.md
// This file tests that ALL registries documentation examples compile correctly
import com.hypixel.hytale.event.EventPriority;
import com.hypixel.hytale.event.EventRegistry;
import com.hypixel.hytale.server.core.command.system.CommandRegistry;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.task.TaskRegistry;
import com.hypixel.hytale.logger.HytaleLogger;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
/**
* Tests ALL registries.en.md documentation examples
*/
public class RegistriesExample {
// ============================================
// From "EventRegistry" section (lines 43-65)
// ============================================
public void registerEvents(EventRegistry eventRegistry, HytaleLogger logger) {
// Simple registration
eventRegistry.register(PlayerConnectEvent.class, event -> {
logger.at(Level.INFO).log("Player connecting: " + event.getPlayerRef().getUsername());
});
// With priority
eventRegistry.register(
EventPriority.EARLY,
PlayerConnectEvent.class,
this::onPlayerConnect
);
}
private void onPlayerConnect(PlayerConnectEvent event) {
// handler
}
// ============================================
// From "CommandRegistry" section (lines 30-38)
// Note: Documentation says register() but should be registerCommand()
// ============================================
public void registerCommands(CommandRegistry commandRegistry) {
// getCommandRegistry().registerCommand(new MyCommand());
// Note: Cannot test without actual command class
}
// ============================================
// From "TaskRegistry" section (lines 94-129)
// ============================================
public void registerTasks(TaskRegistry taskRegistry, HytaleLogger logger) {
// Async operation
CompletableFuture<Void> task = CompletableFuture.runAsync(() -> {
// Background work
});
taskRegistry.registerTask(task);
// Delayed operation (3 seconds)
CompletableFuture.delayedExecutor(3, TimeUnit.SECONDS)
.execute(() -> {
logger.at(Level.INFO).log("Delayed task executed!");
});
// Repeating operation (every 5 minutes)
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture<?> repeating = scheduler.scheduleAtFixedRate(() -> {
logger.at(Level.INFO).log("Repeating task!");
}, 0, 5, TimeUnit.MINUTES);
taskRegistry.registerTask((ScheduledFuture<Void>) repeating);
}
// ============================================
// From "Registration Timing" section (lines 159-176)
// ============================================
// Note: These examples show plugin lifecycle methods
// setup() - Configuration only
// start() - Register everything here
}

View File

@@ -0,0 +1,168 @@
package org.example.docs.coreconcepts;
// Example from: hytale-docs/content/core-concepts/threading.en.md
// This file tests that ALL threading documentation examples compile correctly
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.event.EventRegistry;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.event.events.player.PlayerChatEvent;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
/**
* Tests ALL threading.en.md documentation examples
*/
public class ThreadingExample {
// ============================================
// From "Per-World Threading" section (lines 17-25)
// ============================================
public void perWorldThreadingExample(Player player) {
BlockType blockType = null; // Placeholder
Object position = null; // Placeholder
World world = player.getWorld();
// Check if we're on this world's thread
if (world.isInThread()) {
// Safe to modify world state
// world.setBlock(position, blockType);
}
}
// ============================================
// From "Async Operations" section (lines 35-49)
// Note: Fixed to use PlayerRef.sendMessage() instead of getPlayer()
// ============================================
public void asyncOperationsExample(Player player) {
PlayerRef ref = player.getPlayerRef();
World world = player.getWorld();
CompletableFuture.supplyAsync(() -> {
// This runs on a worker thread
return loadDataFromDatabase(ref.getUuid());
}).thenAccept(data -> {
// PlayerRef.sendMessage() is thread-safe
ref.sendMessage(Message.raw("Data loaded: " + data));
});
}
private String loadDataFromDatabase(UUID uuid) {
return "sample data";
}
// ============================================
// From "PlayerRef" section (lines 63-80)
// Note: Fixed - removed getWorld() and getPlayer() which don't exist
// ============================================
public void playerRefExample(Player player) {
// Store reference (thread-safe)
PlayerRef playerRef = player.getPlayerRef();
// Safe to access on any thread:
UUID uuid = playerRef.getUuid();
String username = playerRef.getUsername();
String language = playerRef.getLanguage();
// Get current world UUID (may change if player switches worlds)
UUID worldUuid = playerRef.getWorldUuid();
// Send message directly (thread-safe)
playerRef.sendMessage(Message.raw("Hello!"));
// For ECS operations, get the entity reference
Ref<EntityStore> entityRef = playerRef.getReference(); // null if not in world
}
// ============================================
// From "Checking Thread Context" section (lines 84-94)
// ============================================
public void checkThreadContextExample(Player player) {
World world = player.getWorld();
// Check if on world's thread
if (!world.isInThread()) {
throw new IllegalStateException("Must be called from world thread!");
}
// Debug: print current thread
System.out.println("Current thread: " + Thread.currentThread().getName());
}
// ============================================
// From "Database Operations" section (lines 100-115)
// Note: Fixed to use PlayerRef.sendMessage()
// ============================================
public void savePlayer(Player player) {
PlayerRef ref = player.getPlayerRef();
Object data = collectPlayerData(player);
CompletableFuture.runAsync(() -> {
// Runs on worker thread
saveToDatabase(ref.getUuid(), data);
}).thenRun(() -> {
// Notify player (PlayerRef.sendMessage is thread-safe)
ref.sendMessage(Message.raw("Saved!"));
});
}
private Object collectPlayerData(Player player) { return null; }
private void saveToDatabase(UUID uuid, Object data) {}
// ============================================
// From "Loading Data on Join" section (lines 120-138)
// Note: Fixed to use Ref<EntityStore> instead of Player
// ============================================
public void loadingDataOnJoinExample(EventRegistry eventRegistry) {
eventRegistry.register(PlayerConnectEvent.class, event -> {
PlayerRef ref = event.getPlayerRef();
World world = event.getWorld();
CompletableFuture.supplyAsync(() -> {
// Load data on worker thread
return loadFromDatabase(ref.getUuid());
}).thenAccept(data -> {
if (world != null && data != null) {
world.execute(() -> {
// Back on world thread - safe to access ECS
Ref<EntityStore> entityRef = ref.getReference();
if (entityRef != null) {
applyData(entityRef, data);
}
});
}
});
});
}
private Object loadFromDatabase(UUID uuid) { return null; }
private void applyData(Ref<EntityStore> entityRef, Object data) {}
// ============================================
// From "Async Events" section (lines 143-156)
// Note: PlayerChatEvent has String key, so use registerAsyncGlobal
// ============================================
public void asyncEventsExample(EventRegistry eventRegistry) {
eventRegistry.registerAsyncGlobal(
PlayerChatEvent.class,
future -> future.thenApply(event -> {
// This runs asynchronously
// Can perform slow operations here
String filtered = filterMessage(event.getContent());
event.setContent(filtered);
return event;
})
);
}
private String filterMessage(String content) {
return content;
}
}

View File

@@ -0,0 +1,217 @@
package org.example.docs.effects;
// Example from: hytale-docs/content/effects/dynamic-lights.en.md
// Tests the Dynamic Lights documentation examples
//
// FINDINGS:
// - getTaskRegistry().schedule() does NOT exist - TaskRegistry only has registerTask()
// - ref.getPlayer() does NOT exist - PlayerRef doesn't have this method
// - player.getEntityRef() does NOT exist - use player.getReference()
// - Need to use standard Java concurrency for scheduling
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.protocol.ColorLight;
import com.hypixel.hytale.server.core.modules.entity.component.DynamicLight;
import com.hypixel.hytale.server.core.modules.entity.component.PersistentDynamicLight;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Tests Dynamic Lights documentation examples
*/
public class DynamicLightsExample extends JavaPlugin {
private ScheduledExecutorService scheduler;
public DynamicLightsExample(JavaPluginInit init) {
super(init);
}
@Override
protected void start() {
scheduler = Executors.newSingleThreadScheduledExecutor();
}
@Override
protected void shutdown() {
if (scheduler != null) {
scheduler.shutdown();
}
}
// =============================================
// ColorLight Structure (DOCUMENTED - CORRECT)
// =============================================
public void colorLightStructure() {
ColorLight light = new ColorLight(
(byte) 15, // Radius (0-255)
(byte) 255, // Red (0-255)
(byte) 100, // Green (0-255)
(byte) 50 // Blue (0-255)
);
}
// =============================================
// DynamicLight Component (DOCUMENTED - CORRECT)
// =============================================
public void addDynamicLight(Ref<EntityStore> entityRef, ComponentAccessor<EntityStore> componentAccessor) {
ColorLight light = new ColorLight(
(byte) 15,
(byte) 255,
(byte) 100,
(byte) 50
);
DynamicLight dynamicLight = new DynamicLight(light);
componentAccessor.putComponent(
entityRef,
DynamicLight.getComponentType(),
dynamicLight
);
}
// =============================================
// PersistentDynamicLight Component (DOCUMENTED - CORRECT)
// =============================================
public void addPersistentLight(Ref<EntityStore> entityRef, ComponentAccessor<EntityStore> componentAccessor, ColorLight light) {
PersistentDynamicLight persistentLight = new PersistentDynamicLight(light);
componentAccessor.putComponent(
entityRef,
PersistentDynamicLight.getComponentType(),
persistentLight
);
}
// =============================================
// Updating Light Properties (CORRECT - uses Store<EntityStore>)
// =============================================
public void updateLight(Ref<EntityStore> entityRef, Store<EntityStore> store) {
DynamicLight dynamicLight = store.getComponent(
entityRef,
DynamicLight.getComponentType()
);
if (dynamicLight != null) {
ColorLight newLight = new ColorLight(
(byte) 20,
(byte) 0,
(byte) 255,
(byte) 100
);
dynamicLight.setColorLight(newLight);
}
}
// =============================================
// Removing Light (DOCUMENTED - CORRECT)
// =============================================
public void removeLight(Ref<EntityStore> entityRef, ComponentAccessor<EntityStore> componentAccessor) {
componentAccessor.removeComponent(
entityRef,
DynamicLight.getComponentType()
);
}
// =============================================
// Common Light Configurations (DOCUMENTED - CORRECT)
// =============================================
public void lightConfigurations() {
ColorLight fireLight = new ColorLight((byte) 12, (byte) 255, (byte) 150, (byte) 50);
ColorLight iceLight = new ColorLight((byte) 10, (byte) 100, (byte) 200, (byte) 255);
ColorLight magicLight = new ColorLight((byte) 15, (byte) 200, (byte) 50, (byte) 255);
ColorLight healLight = new ColorLight((byte) 10, (byte) 50, (byte) 255, (byte) 100);
}
// =============================================
// Adding a Glow Effect to a Player - CORRECTED VERSION
// DOCUMENTED CODE HAS ISSUES:
// - ref.getPlayer() does NOT exist
// - getTaskRegistry().schedule() does NOT exist
// - player.getEntityRef() should be player.getReference()
// =============================================
public void addGlowEffectCorrected(Player player, ComponentAccessor<EntityStore> componentAccessor) {
PlayerRef ref = player.getPlayerRef();
World world = player.getWorld();
ColorLight glow = new ColorLight(
(byte) 8,
(byte) 255,
(byte) 215,
(byte) 0
);
DynamicLight light = new DynamicLight(glow);
// CORRECT: Use player.getReference() instead of player.getEntityRef()
Ref<EntityStore> entityRef = player.getReference();
componentAccessor.putComponent(
entityRef,
DynamicLight.getComponentType(),
light
);
// CORRECT: Use standard Java scheduling instead of getTaskRegistry().schedule()
scheduler.schedule(() -> {
world.execute(() -> {
// Check if entity is still valid
if (entityRef.isValid()) {
componentAccessor.removeComponent(
entityRef,
DynamicLight.getComponentType()
);
}
});
}, 3, TimeUnit.SECONDS);
}
// =============================================
// Pulsing Light Effect (CORRECTED - uses Store<EntityStore>)
// =============================================
private final Map<Ref<EntityStore>, Integer> pulsingEntities = new ConcurrentHashMap<>();
public void onTick(float deltaTime, Store<EntityStore> store) {
for (Map.Entry<Ref<EntityStore>, Integer> entry : pulsingEntities.entrySet()) {
Ref<EntityStore> entityRef = entry.getKey();
int tick = entry.getValue() + 1;
entry.setValue(tick);
double pulse = Math.sin(tick * 0.1) * 0.5 + 0.5;
byte radius = (byte) (5 + pulse * 10);
ColorLight light = new ColorLight(
radius,
(byte) 255,
(byte) (int)(100 + pulse * 100),
(byte) 50
);
DynamicLight dynamicLight = store.getComponent(
entityRef,
DynamicLight.getComponentType()
);
if (dynamicLight != null) {
dynamicLight.setColorLight(light);
}
}
}
public void startPulsingLight(Ref<EntityStore> entityRef) {
pulsingEntities.put(entityRef, 0);
}
public void stopPulsingLight(Ref<EntityStore> entityRef) {
pulsingEntities.remove(entityRef);
}
}

View File

@@ -0,0 +1,81 @@
package org.example.docs.effects;
// Example from: hytale-docs/content/effects/_index.en.md
// Tests the Effects index page documentation examples
//
// FINDINGS:
// - Vector3d is from com.hypixel.hytale.math.vector.Vector3d, NOT org.joml
// - store.getComponent needs a Store<EntityStore>, not EntityStore directly
// - player.getEntityRef() does NOT exist - use player.getReference()
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.universe.world.ParticleUtil;
import com.hypixel.hytale.protocol.ColorLight;
import com.hypixel.hytale.server.core.modules.entity.component.DynamicLight;
import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent;
import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.math.vector.Vector3d;
/**
* Tests Effects index documentation examples
*/
public class EffectsIndexExample extends JavaPlugin {
public EffectsIndexExample(JavaPluginInit init) {
super(init);
}
// =============================================
// Quick Start: Spawning Particles
// =============================================
public void spawnParticleExample(Vector3d position, ComponentAccessor<EntityStore> componentAccessor) {
// Spawn particles at a position
ParticleUtil.spawnParticleEffect(
"explosion_small", // Particle system ID
position, // Vector3d position
componentAccessor
);
}
// =============================================
// Quick Start: Adding Dynamic Light
// =============================================
public void dynamicLightExample(Ref<EntityStore> entityRef, ComponentAccessor<EntityStore> componentAccessor) {
// Create a colored light (radius, R, G, B)
ColorLight light = new ColorLight(
(byte) 15, // Radius
(byte) 255, // Red
(byte) 100, // Green
(byte) 50 // Blue
);
// Add to entity
DynamicLight dynamicLight = new DynamicLight(light);
componentAccessor.putComponent(entityRef, DynamicLight.getComponentType(), dynamicLight);
}
// =============================================
// Quick Start: Applying Entity Effects
// NOTE: Documentation uses store.getComponent() but EntityStore != Store<EntityStore>
// The actual API requires Store<EntityStore> which is obtained from EntityStore.getStore()
// =============================================
public void entityEffectExample(Ref<EntityStore> entityRef, Store<EntityStore> store, ComponentAccessor<EntityStore> componentAccessor) {
// Get effect controller
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
// Apply effect
EntityEffect effect = EntityEffect.getAssetMap().getAsset("fire_resistance");
if (controller != null && effect != null) {
controller.addEffect(entityRef, effect, componentAccessor);
}
}
}

View File

@@ -0,0 +1,318 @@
package org.example.docs.effects;
// Example from: hytale-docs/content/effects/entity-effects.en.md
// Tests the Entity Effects documentation examples
//
// FINDINGS (DOCUMENTED CODE HAS ISSUES):
// - OverlapBehavior is at com.hypixel.hytale.server.core.asset.type.entityeffect.config.OverlapBehavior
// - BuffCommand.execute() returns void, should return CompletableFuture<Void>
// - ctx.getArg("name", Type.class) does NOT exist - use requiredArg.get(ctx)
// - target.getPlayer() does NOT exist - PlayerRef doesn't have this method
// - ctx.getSender() should be ctx.sender()
// - target.getName() should be target.getUsername()
// - player.sendMessage("text") should use Message.raw("text")
// - player.getEntityRef() does NOT exist - use player.getReference()
// - store.getComponent() requires Store<EntityStore>, not EntityStore
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent;
import com.hypixel.hytale.server.core.asset.type.entityeffect.config.OverlapBehavior;
import com.hypixel.hytale.server.core.asset.type.entityeffect.config.RemovalBehavior;
import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.ParticleUtil;
import com.hypixel.hytale.server.core.modules.entity.component.DynamicLight;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.protocol.ColorLight;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
import java.util.concurrent.CompletableFuture;
/**
* Tests Entity Effects documentation examples
*/
public class EntityEffectsExample extends JavaPlugin {
public EntityEffectsExample(JavaPluginInit init) {
super(init);
}
// =============================================
// Accessing Effect Assets (DOCUMENTED - CORRECT)
// =============================================
public void accessEffectAssets() {
// Get effect by ID
EntityEffect effect = EntityEffect.getAssetMap().getAsset("fire_resistance");
// Get effect index for operations
if (effect != null) {
int effectIndex = EntityEffect.getAssetMap().getIndex(effect.getId());
}
}
// =============================================
// EffectControllerComponent - Getting controller
// CORRECTED: Uses Store<EntityStore> instead of EntityStore
// =============================================
public void getEffectController(Ref<EntityStore> entityRef, Store<EntityStore> store) {
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
}
// =============================================
// Adding Effects - Basic Addition (CORRECTED)
// =============================================
public void addBasicEffect(Ref<EntityStore> entityRef, Store<EntityStore> store, ComponentAccessor<EntityStore> componentAccessor) {
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
EntityEffect effect = EntityEffect.getAssetMap().getAsset("speed_boost");
if (controller != null && effect != null) {
controller.addEffect(
entityRef,
effect,
componentAccessor
);
}
}
// =============================================
// Adding Effects - With Custom Duration (CORRECTED)
// =============================================
public void addEffectWithDuration(Ref<EntityStore> entityRef, Store<EntityStore> store, ComponentAccessor<EntityStore> componentAccessor) {
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
EntityEffect effect = EntityEffect.getAssetMap().getAsset("speed_boost");
if (controller != null && effect != null) {
controller.addEffect(
entityRef,
effect,
100.0f, // Duration in seconds
OverlapBehavior.EXTEND, // How to handle overlap
componentAccessor
);
}
}
// =============================================
// Adding Effects - Infinite Effects (CORRECTED)
// =============================================
public void addInfiniteEffect(Ref<EntityStore> entityRef, Store<EntityStore> store, ComponentAccessor<EntityStore> componentAccessor) {
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
EntityEffect effect = EntityEffect.getAssetMap().getAsset("permanent_buff");
if (controller != null && effect != null) {
controller.addInfiniteEffect(
entityRef,
EntityEffect.getAssetMap().getIndex(effect.getId()),
effect,
componentAccessor
);
}
}
// =============================================
// Removing Effects (CORRECTED)
// =============================================
public void removeEffect(Ref<EntityStore> entityRef, Store<EntityStore> store, ComponentAccessor<EntityStore> componentAccessor) {
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
EntityEffect effect = EntityEffect.getAssetMap().getAsset("speed_boost");
if (controller != null && effect != null) {
controller.removeEffect(
entityRef,
EntityEffect.getAssetMap().getIndex(effect.getId()),
componentAccessor
);
}
}
// =============================================
// Overlap Behaviors Usage (CORRECTED)
// =============================================
public void overlapBehaviorExamples(Ref<EntityStore> entityRef, Store<EntityStore> store, ComponentAccessor<EntityStore> componentAccessor) {
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
EntityEffect effect = EntityEffect.getAssetMap().getAsset("buff");
if (controller != null && effect != null) {
// Extend duration if already applied
controller.addEffect(entityRef, effect, 30.0f, OverlapBehavior.EXTEND, componentAccessor);
// Replace with fresh duration
controller.addEffect(entityRef, effect, 30.0f, OverlapBehavior.OVERWRITE, componentAccessor);
// Only apply if not already active
controller.addEffect(entityRef, effect, 30.0f, OverlapBehavior.IGNORE, componentAccessor);
}
}
// =============================================
// Buff Command - CORRECTED VERSION
// Fixed: return type, argument API, sender API, PlayerRef API
// =============================================
public static class BuffCommandCorrected extends AbstractCommand {
private final RequiredArg<PlayerRef> playerArg;
private final RequiredArg<String> effectArg;
private final RequiredArg<Integer> durationArg;
// Would be injected during plugin init
private Store<EntityStore> store;
private ComponentAccessor<EntityStore> componentAccessor;
public BuffCommandCorrected() {
super("buff", "effects.command.buff.description");
this.playerArg = withRequiredArg("player", "Target player", ArgTypes.PLAYER_REF);
this.effectArg = withRequiredArg("effect", "Effect ID", ArgTypes.STRING);
this.durationArg = withRequiredArg("duration", "Duration in seconds", ArgTypes.INTEGER);
}
@Override
protected CompletableFuture<Void> execute(CommandContext ctx) {
PlayerRef target = playerArg.get(ctx);
String effectId = effectArg.get(ctx);
int duration = durationArg.get(ctx);
// CORRECTED: Use target.getReference() - PlayerRef doesn't have getPlayer()
Ref<EntityStore> entityRef = target.getReference();
if (entityRef == null || !entityRef.isValid()) {
ctx.sender().sendMessage(Message.raw("Player not found"));
return null;
}
EntityEffect effect = EntityEffect.getAssetMap().getAsset(effectId);
if (effect == null) {
ctx.sender().sendMessage(Message.raw("Unknown effect: " + effectId));
return null;
}
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null) {
controller.addEffect(
entityRef,
effect,
(float) duration,
OverlapBehavior.EXTEND,
componentAccessor
);
}
// CORRECTED: target.getUsername() instead of target.getName()
ctx.sender().sendMessage(Message.raw("Applied " + effectId + " to " + target.getUsername()));
return null;
}
}
// =============================================
// Damage Over Time Effect - CORRECTED
// Uses player.getReference() and Message.raw()
// =============================================
public void applyPoison(Player player, float duration, Store<EntityStore> store, ComponentAccessor<EntityStore> componentAccessor) {
EntityEffect poison = EntityEffect.getAssetMap().getAsset("poison");
// CORRECTED: Use player.getReference() instead of player.getEntityRef()
Ref<EntityStore> entityRef = player.getReference();
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null && poison != null) {
controller.addEffect(
entityRef,
poison,
duration,
OverlapBehavior.EXTEND,
componentAccessor
);
}
// CORRECTED: Use Message.raw()
player.getPlayerRef().sendMessage(Message.raw("You have been poisoned!"));
}
// =============================================
// Clear All Effects - CORRECTED
// =============================================
public void clearAllEffects(Player player, Store<EntityStore> store, ComponentAccessor<EntityStore> componentAccessor) {
Ref<EntityStore> entityRef = player.getReference();
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null) {
controller.clearEffects(
entityRef,
componentAccessor
);
}
// CORRECTED: Use Message.raw()
player.getPlayerRef().sendMessage(Message.raw("All effects cleared!"));
}
// =============================================
// Combining with Other Effects - CORRECTED
// player.getPosition() does NOT exist on Player
// =============================================
public void applyMagicBuff(Player player, Store<EntityStore> store, ComponentAccessor<EntityStore> componentAccessor) {
EntityEffect buff = EntityEffect.getAssetMap().getAsset("magic_power");
Ref<EntityStore> entityRef = player.getReference();
EffectControllerComponent controller = store.getComponent(
entityRef,
EffectControllerComponent.getComponentType()
);
if (controller != null && buff != null) {
controller.addEffect(entityRef, buff, componentAccessor);
}
// Add visual glow
ColorLight glow = new ColorLight((byte) 10, (byte) 200, (byte) 50, (byte) 255);
DynamicLight light = new DynamicLight(glow);
componentAccessor.putComponent(
entityRef,
DynamicLight.getComponentType(),
light
);
// NOTE: player.getPosition() does NOT exist
// Would need to get position from TransformComponent via the entity store
}
}

View File

@@ -0,0 +1,144 @@
package org.example.docs.effects;
// Example from: hytale-docs/content/effects/particles.en.md
// Tests the Particles documentation examples
//
// FINDINGS (DOCUMENTED CODE HAS ISSUES):
// - Vector3d is from com.hypixel.hytale.math.vector.Vector3d, NOT org.joml
// - event.getPosition().x() - Vector3i uses getX(), not x() (but also has public fields x,y,z)
// - target.getPosition() - Entity doesn't have getPosition() directly
// - player.getPosition() - Player doesn't have getPosition()
// - player.getUuid() - should be player.getPlayerRef().getUuid()
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.universe.world.ParticleUtil;
import com.hypixel.hytale.server.core.asset.type.particle.config.ParticleSystem;
import com.hypixel.hytale.server.core.asset.type.particle.config.ParticleSpawner;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.entity.Entity;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.event.events.ecs.BreakBlockEvent;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Vector3i;
import java.awt.Color;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.UUID;
/**
* Tests Particles documentation examples
*/
public class ParticlesExample extends JavaPlugin {
private ComponentAccessor<EntityStore> componentAccessor;
public ParticlesExample(JavaPluginInit init) {
super(init);
}
// =============================================
// Basic Spawn (DOCUMENTED - CORRECT)
// =============================================
public void basicSpawn(Vector3d position) {
ParticleUtil.spawnParticleEffect(
"explosion_small",
position,
componentAccessor
);
}
// =============================================
// Spawn for specific players (DOCUMENTED - CORRECT)
// =============================================
public void spawnForPlayers(Vector3d position, List<Ref<EntityStore>> playerRefs) {
ParticleUtil.spawnParticleEffect(
"magic_trail",
position,
playerRefs,
componentAccessor
);
}
// =============================================
// Advanced: With source entity reference (DOCUMENTED - CORRECT)
// =============================================
public void advancedSpawn(Vector3d position, Ref<EntityStore> sourceEntityRef, List<Ref<EntityStore>> playerRefs) {
ParticleUtil.spawnParticleEffect(
"attack_swing",
position,
sourceEntityRef,
playerRefs,
componentAccessor
);
}
// =============================================
// Accessing Particle Assets (DOCUMENTED - CORRECT)
// =============================================
public void accessAssets() {
ParticleSystem system = ParticleSystem.getAssetMap().getAsset("explosion_large");
ParticleSpawner spawner = ParticleSpawner.getAssetMap().getAsset("fire_spawner");
}
// =============================================
// Event-Based Particles: Block Break
// DOCUMENTED CODE HAS ISSUE:
// event.getPosition().x() should work since Vector3i has public field x
// But documentation uses x() method which doesn't exist
// CORRECT: Use .x, .y, .z public fields OR getX(), getY(), getZ()
// =============================================
public void onBlockBreak(BreakBlockEvent event) {
// CORRECTED: Use getTargetBlock(), not getPosition()
Vector3i blockPos = event.getTargetBlock();
// Use public fields x, y, z
Vector3d pos = new Vector3d(
blockPos.x + 0.5,
blockPos.y + 0.5,
blockPos.z + 0.5
);
ParticleUtil.spawnParticleEffect(
"block_break_particles",
pos,
componentAccessor
);
}
// =============================================
// Aura Manager - CORRECTED
// player.getUuid() should be player.getPlayerRef().getUuid()
// player.getPosition() doesn't exist
// =============================================
private final Map<UUID, Boolean> activeAuras = new HashMap<>();
public void enableAura(Player player) {
// CORRECTED: Use player.getPlayerRef().getUuid()
activeAuras.put(player.getPlayerRef().getUuid(), true);
}
// NOTE: Tick-based aura spawning would need to get player position
// from TransformComponent, not from player.getPosition()
// =============================================
// Efficient spawning pattern (DOCUMENTED - CORRECT pattern)
// =============================================
public void spawnEfficiently(Vector3d position, List<Ref<EntityStore>> nearbyPlayerRefs) {
if (!nearbyPlayerRefs.isEmpty()) {
ParticleUtil.spawnParticleEffect(
"my_effect",
position,
nearbyPlayerRefs,
componentAccessor
);
}
}
}

View File

@@ -0,0 +1,49 @@
package org.example.docs.entities;
// Example from: hytale-docs/content/entities/_index.en.md
// Tests the "Quick Example" code block
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
/**
* Tests _index.en.md documentation
*
* NOTE: The doc shows player.getPosition() but this method doesn't exist.
* Position is accessed via getTransformComponent().getPosition() (deprecated)
* or through ECS component access.
*/
public class EntitiesIndexExample extends JavaPlugin {
public EntitiesIndexExample(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
// From "Quick Example" section - CORRECTED version
getEventRegistry().register(PlayerConnectEvent.class, event -> {
// getPlayer() is @Deprecated but exists
Player player = event.getPlayer();
if (player != null) {
// getPosition() doesn't exist on Player - use TransformComponent
// getTransformComponent() is deprecated but works
TransformComponent transform = player.getTransformComponent();
Vector3d pos = transform.getPosition();
// Get world - this works
World world = player.getWorld();
// sendMessage requires Message object, not String
player.sendMessage(Message.raw("Welcome at " + pos.toString()));
}
});
}
}

View File

@@ -0,0 +1,175 @@
package org.example.docs.entities;
// Example from: hytale-docs/content/entities/entity-components.en.md
// Tests the entity component documentation examples
//
// CRITICAL FINDING:
// The documentation shows: entityRef.getComponent(type) and entityRef.hasComponent(type)
// But Ref<EntityStore> does NOT have these methods!
// Correct pattern: store.getComponent(ref, type) where store = ref.getStore()
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.entity.component.DisplayNameComponent;
import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox;
import com.hypixel.hytale.server.core.modules.entity.component.EntityScaleComponent;
import com.hypixel.hytale.server.core.modules.entity.component.DynamicLight;
import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation;
import com.hypixel.hytale.server.core.modules.entity.component.Invulnerable;
import com.hypixel.hytale.server.core.modules.entity.component.Intangible;
import com.hypixel.hytale.server.core.modules.entity.component.Interactable;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.math.shape.Box;
import com.hypixel.hytale.protocol.ColorLight;
import com.hypixel.hytale.server.core.Message;
import java.util.logging.Level;
/**
* Tests entity-components.en.md documentation
*
* IMPORTANT: The documentation shows entityRef.getComponent(type) but this is WRONG!
* The correct pattern is:
* Store<EntityStore> store = ref.getStore();
* Component comp = store.getComponent(ref, ComponentType);
*
* Ref<EntityStore> only has:
* - getStore() - returns Store<EntityStore>
* - getIndex() - returns int
* - isValid() - returns boolean
* - validate() - throws if invalid
*/
public class EntityComponentsExample extends JavaPlugin {
public EntityComponentsExample(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef playerRef = event.getPlayerRef();
// Get ECS reference
Ref<EntityStore> ref = playerRef.getReference();
if (ref == null || !ref.isValid()) {
return;
}
// CORRECT PATTERN: get store, then get component from store
Store<EntityStore> store = ref.getStore();
// =============================================
// TransformComponent - CORRECT PATTERN
// =============================================
ComponentType<EntityStore, TransformComponent> transformType =
TransformComponent.getComponentType();
TransformComponent transform = store.getComponent(ref, transformType);
if (transform != null) {
// Get current position
Vector3d pos = transform.getPosition();
getLogger().at(Level.INFO).log("Entity at: " + pos.x + ", " + pos.y + ", " + pos.z);
// Get rotation
Vector3f rotation = transform.getRotation();
}
// =============================================
// BoundingBox - CORRECT PATTERN
// =============================================
ComponentType<EntityStore, BoundingBox> boundsType =
BoundingBox.getComponentType();
BoundingBox bounds = store.getComponent(ref, boundsType);
if (bounds != null) {
Box box = bounds.getBoundingBox();
// Box has methods like width(), height(), depth()
}
// =============================================
// DisplayNameComponent - CORRECT PATTERN
// =============================================
ComponentType<EntityStore, DisplayNameComponent> nameType =
DisplayNameComponent.getComponentType();
DisplayNameComponent nameComp = store.getComponent(ref, nameType);
if (nameComp != null) {
Message name = nameComp.getDisplayName();
}
// =============================================
// EntityScaleComponent - CORRECT PATTERN
// =============================================
ComponentType<EntityStore, EntityScaleComponent> scaleType =
EntityScaleComponent.getComponentType();
EntityScaleComponent scale = store.getComponent(ref, scaleType);
if (scale != null) {
float currentScale = scale.getScale();
// scale.setScale(2.0f); // Would make entity 2x larger
}
// =============================================
// DynamicLight - CORRECT PATTERN
// =============================================
ComponentType<EntityStore, DynamicLight> lightType =
DynamicLight.getComponentType();
DynamicLight light = store.getComponent(ref, lightType);
if (light != null) {
ColorLight colorLight = light.getColorLight();
// colorLight has public fields: radius, red, green, blue
}
// =============================================
// HeadRotation - CORRECT PATTERN
// =============================================
ComponentType<EntityStore, HeadRotation> headType =
HeadRotation.getComponentType();
HeadRotation head = store.getComponent(ref, headType);
if (head != null) {
Vector3f rot = head.getRotation();
Vector3d direction = head.getDirection();
}
// =============================================
// Singleton Marker Components
// =============================================
// To check if an entity has a marker component, use archetype
Archetype<EntityStore> archetype = store.getArchetype(ref);
// Check for Invulnerable
ComponentType<EntityStore, Invulnerable> invulnType =
Invulnerable.getComponentType();
boolean isInvulnerable = archetype.contains(invulnType);
// Check for Intangible
ComponentType<EntityStore, Intangible> intangibleType =
Intangible.getComponentType();
boolean isIntangible = archetype.contains(intangibleType);
// Check for Interactable
ComponentType<EntityStore, Interactable> interactableType =
Interactable.getComponentType();
boolean isInteractable = archetype.contains(interactableType);
// =============================================
// WRONG PATTERNS (from the documentation):
// =============================================
// entityRef.getComponent(transformType); // WRONG - Ref doesn't have getComponent
// entityRef.hasComponent(type); // WRONG - Ref doesn't have hasComponent
// entityRef.getAccessor(); // WRONG - Ref doesn't have getAccessor
});
}
}

View File

@@ -0,0 +1,199 @@
package org.example.docs.entities;
// Example from: hytale-docs/content/entities/entity-hierarchy.en.md
// This file tests the entity hierarchy documentation examples
//
// CRITICAL FINDING:
// The documentation describes a traditional OOP entity API that does NOT exist.
// Hytale uses ECS (Entity Component System) architecture where:
// - Entity is a Component<EntityStore>, not a standalone class with methods
// - Position is accessed via TransformComponent
// - Health, damage, effects are managed through separate systems/components
// - Methods like getPosition(), setPosition(), getHealth(), damage() DON'T EXIST
import com.hypixel.hytale.server.core.entity.Entity;
import com.hypixel.hytale.server.core.entity.LivingEntity;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.inventory.Inventory;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.math.vector.Vector3d;
import java.util.UUID;
import java.util.logging.Level;
/**
* Tests entity-hierarchy.en.md documentation
*
* IMPORTANT: Most methods described in the documentation do NOT exist.
* This file demonstrates what ACTUALLY works vs what the doc claims.
*/
public class EntityHierarchyExample extends JavaPlugin {
public EntityHierarchyExample(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
// Test Player methods that actually exist
getEventRegistry().register(PlayerConnectEvent.class, event -> {
Player player = event.getPlayer();
PlayerRef playerRef = event.getPlayerRef();
World world = event.getWorld();
if (player != null) {
// =============================================
// METHODS THAT ACTUALLY EXIST on Player:
// =============================================
// Send message (requires Message object, not String!)
player.sendMessage(Message.raw("Hello!"));
// Check permissions - EXISTS
boolean hasAdmin = player.hasPermission("admin.teleport");
// Get display name - EXISTS
String displayName = player.getDisplayName();
// Get world - EXISTS on Entity
World playerWorld = player.getWorld();
// Get inventory - EXISTS on LivingEntity
Inventory inventory = player.getInventory();
// Get UUID - EXISTS (but deprecated)
UUID uuid = player.getUuid();
// wasRemoved() - EXISTS (not isRemoved())
boolean removed = player.wasRemoved();
// getTransformComponent() - EXISTS but deprecated
TransformComponent transform = player.getTransformComponent();
Vector3d position = transform.getPosition();
// =============================================
// METHODS THAT DON'T EXIST (documented but wrong):
// =============================================
// player.getPosition() - DOESN'T EXIST
// player.setPosition() - DOESN'T EXIST (use moveTo with ECS)
// player.isValid() - DOESN'T EXIST
// player.getId() - use getNetworkId() (deprecated)
// player.getEntityType() - DOESN'T EXIST
// player.hasComponent() - DOESN'T EXIST (Entity IS a Component)
// player.getComponent() - DOESN'T EXIST (use Store.getComponent)
// player.getName() - use getDisplayName() or playerRef.getUsername()
// player.sendMessage(String) - requires Message.raw()
// player.sendTitle() - DOESN'T EXIST
// player.sendActionBar() - DOESN'T EXIST
// player.getHealth() - DOESN'T EXIST
// player.setHealth() - DOESN'T EXIST
// player.damage() - DOESN'T EXIST
// player.addEffect() - DOESN'T EXIST
// player.getVelocity() - DOESN'T EXIST (use Velocity component)
// player.setVelocity() - DOESN'T EXIST
// player.giveItem() - DOESN'T EXIST (use inventory.addItem)
// player.kick() - DOESN'T EXIST
}
// =============================================
// PlayerRef - what actually exists:
// =============================================
String username = playerRef.getUsername();
UUID uuid = playerRef.getUuid();
UUID worldUuid = playerRef.getWorldUuid();
// Send message via PlayerRef - EXISTS
playerRef.sendMessage(Message.raw("Hello via PlayerRef!"));
// Get ECS reference - EXISTS
Ref<EntityStore> ref = playerRef.getReference();
if (ref != null && ref.isValid()) {
// This is how you access components in ECS
Store<EntityStore> store = ref.getStore();
Player playerComponent = store.getComponent(ref, Player.getComponentType());
TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType());
}
});
}
// =============================================
// LivingEntity - what actually exists:
// =============================================
public void livingEntityExample(LivingEntity living) {
// getInventory() - EXISTS
Inventory inventory = living.getInventory();
// getWorld() - EXISTS (inherited from Entity)
World world = living.getWorld();
// wasRemoved() - EXISTS
boolean removed = living.wasRemoved();
// getCurrentFallDistance() - EXISTS
double fallDistance = living.getCurrentFallDistance();
// getStatModifiersManager() - EXISTS
var statManager = living.getStatModifiersManager();
// =============================================
// Methods that DON'T EXIST on LivingEntity:
// =============================================
// getHealth(), setHealth(), getMaxHealth(), setMaxHealth()
// isDead(), heal()
// damage(double, DamageSource)
// addEffect(), removeEffect(), hasEffect(), clearEffects()
// getVelocity(), setVelocity(), knockback()
// isOnGround(), isSprinting(), isSneaking()
// setInvulnerable(), isInvulnerable()
// getKiller(), getLastDamageSource()
}
// =============================================
// Entity - what actually exists:
// =============================================
public void entityExample(Entity entity) {
// getWorld() - EXISTS
World world = entity.getWorld();
// getUuid() - EXISTS but deprecated
UUID uuid = entity.getUuid();
// wasRemoved() - EXISTS (not isRemoved())
boolean removed = entity.wasRemoved();
// remove() - EXISTS
// entity.remove();
// getNetworkId() - EXISTS but deprecated (not getId())
int networkId = entity.getNetworkId();
// getTransformComponent() - EXISTS but deprecated
TransformComponent transform = entity.getTransformComponent();
// getReference() - EXISTS
Ref<EntityStore> ref = entity.getReference();
// =============================================
// Methods that DON'T EXIST on Entity:
// =============================================
// getPosition(), setPosition()
// getRotation(), setRotation()
// getId() - use getNetworkId()
// isValid() - check ref.isValid()
// getEntityType()
// getChunk()
// getBoundingBox()
// hasComponent(), getComponent(), addComponent(), removeComponent()
// isRemoved() - use wasRemoved()
// getTicksLived()
}
}

View File

@@ -0,0 +1,164 @@
package org.example.docs.entities;
// Example from: hytale-docs/content/entities/player-api.en.md
// Tests the Player API documentation examples
//
// FINDINGS:
// Many methods documented do NOT exist on Player class.
// Player is a LivingEntity with limited direct methods.
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.inventory.Inventory;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.protocol.GameMode;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.math.vector.Transform;
import java.util.UUID;
import java.util.logging.Level;
/**
* Tests player-api.en.md documentation
*
* IMPORTANT: Many methods in the documentation DON'T EXIST on Player:
* - getPosition(), setPosition() - use TransformComponent
* - getVelocity(), setVelocity(), addVelocity(), knockback() - don't exist
* - isSprinting(), setSprinting(), isSneaking(), setSneaking() - don't exist
* - isFlying(), setFlying(), isSwimming() - don't exist
* - isOnGround() - doesn't exist
* - isOp(), setOp(), addPermission(), removePermission(), getPermissions() - don't exist
* - giveItem(), dropItem(), getHeldItem() - don't exist
* - openInventory(), closeInventory(), getCursorItem() - don't exist
* - kick() - use playerRef.getPacketHandler().disconnect()
*/
public class PlayerApiExample extends JavaPlugin {
public PlayerApiExample(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef playerRef = event.getPlayerRef();
Player player = event.getPlayer();
// =============================================
// Methods that ACTUALLY EXIST on Player:
// =============================================
if (player != null) {
// getUuid() - EXISTS (deprecated)
UUID uuid = player.getUuid();
// getDisplayName() - EXISTS
String displayName = player.getDisplayName();
// sendMessage(Message) - EXISTS
player.sendMessage(Message.raw("Hello!"));
// hasPermission(String) - EXISTS
boolean hasAdmin = player.hasPermission("admin.perm");
// getGameMode() - EXISTS
GameMode mode = player.getGameMode();
// getInventory() - EXISTS (inherited from LivingEntity)
Inventory inventory = player.getInventory();
// getViewRadius() / getClientViewRadius() - EXISTS
int viewRadius = player.getViewRadius();
int clientViewRadius = player.getClientViewRadius();
// getWorld() - EXISTS (inherited from Entity)
var world = player.getWorld();
}
// =============================================
// PlayerRef - Methods that EXIST:
// =============================================
// getUuid() - EXISTS
UUID uuid = playerRef.getUuid();
// getUsername() - EXISTS
String username = playerRef.getUsername();
// getWorldUuid() - EXISTS
UUID worldUuid = playerRef.getWorldUuid();
// sendMessage(Message) - EXISTS
playerRef.sendMessage(Message.raw("Hello via PlayerRef!"));
// getReference() - EXISTS
Ref<EntityStore> ref = playerRef.getReference();
// getPacketHandler() - EXISTS
var packetHandler = playerRef.getPacketHandler();
// getTransform() - EXISTS
Transform transform = playerRef.getTransform();
if (transform != null) {
Vector3d position = transform.getPosition();
Vector3f rotation = transform.getRotation();
}
// getHeadRotation() - EXISTS
Vector3f headRotation = playerRef.getHeadRotation();
// isValid() - EXISTS
boolean isValid = playerRef.isValid();
// referToServer() - EXISTS
// playerRef.referToServer("play.example.com", 25565);
// getChunkTracker() - EXISTS
var chunkTracker = playerRef.getChunkTracker();
// =============================================
// WRONG PATTERNS (from documentation):
// =============================================
// player.getPosition() - DOESN'T EXIST
// player.setPosition(Vector3d) - DOESN'T EXIST
// player.getVelocity() - DOESN'T EXIST
// player.setVelocity(Vector3d) - DOESN'T EXIST
// player.addVelocity(Vector3d) - DOESN'T EXIST
// player.knockback(Vector3d) - DOESN'T EXIST
// player.isSprinting() - DOESN'T EXIST
// player.setSprinting(boolean) - DOESN'T EXIST
// player.isSneaking() - DOESN'T EXIST
// player.setSneaking(boolean) - DOESN'T EXIST
// player.isFlying() - DOESN'T EXIST
// player.setFlying(boolean) - DOESN'T EXIST
// player.isSwimming() - DOESN'T EXIST
// player.isOnGround() - DOESN'T EXIST
// player.getEyePosition() - DOESN'T EXIST
// player.getEyeDirection() - DOESN'T EXIST
// player.isOp() - DOESN'T EXIST
// player.setOp(boolean) - DOESN'T EXIST
// player.addPermission(String) - DOESN'T EXIST
// player.removePermission(String) - DOESN'T EXIST
// player.getPermissions() - DOESN'T EXIST
// player.giveItem(ItemStack) - DOESN'T EXIST
// player.dropItem(ItemStack) - DOESN'T EXIST
// player.getHeldItem() - DOESN'T EXIST
// player.setHeldItem(ItemStack) - DOESN'T EXIST
// player.openInventory(Inventory) - DOESN'T EXIST
// player.closeInventory() - DOESN'T EXIST
// player.getCursorItem() - DOESN'T EXIST
// player.kick(String) - DOESN'T EXIST - use packetHandler.disconnect()
// Correct way to disconnect a player:
// playerRef.getPacketHandler().disconnect("Reason");
});
}
}

View File

@@ -0,0 +1,320 @@
package org.example.docs.events;
// Example from: hytale-docs/content/events/async-events.en.md
// This file tests that ALL async events documentation examples compile correctly
import com.hypixel.hytale.event.EventPriority;
import com.hypixel.hytale.event.EventRegistry;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.event.events.player.PlayerChatEvent;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.logger.HytaleLogger;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
/**
* Tests async-events.en.md documentation
*
* IMPORTANT NOTE:
* - PlayerChatEvent implements IAsyncEvent<String> - it has a String key
* - Therefore, you MUST use registerAsyncGlobal() not registerAsync()
* - The simple registerAsync(Class, handler) only works for Void key events
*/
public class AsyncEventsExample {
// ============================================
// From "Registering Async Handlers" section
// CORRECTION: Must use registerAsyncGlobal() for PlayerChatEvent
// ============================================
public void registerAsyncHandlers(EventRegistry eventRegistry) {
// PlayerChatEvent has String key - MUST use registerAsyncGlobal()
eventRegistry.registerAsyncGlobal(PlayerChatEvent.class, future -> {
return future.thenApply(event -> {
// This runs asynchronously
String filtered = filterContent(event.getContent());
event.setContent(filtered);
return event;
});
});
}
// ============================================
// From "With Priority" section
// ============================================
public void registerWithPriority(EventRegistry eventRegistry) {
eventRegistry.registerAsyncGlobal(
EventPriority.EARLY,
PlayerChatEvent.class,
future -> future.thenApply(event -> {
// Runs early in async chain
return event;
})
);
}
// ============================================
// From "Sequential Operations" section
// ============================================
public void sequentialOperations(EventRegistry eventRegistry, HytaleLogger logger) {
eventRegistry.registerAsyncGlobal(PlayerChatEvent.class, future -> {
return future
.thenApply(event -> {
// Step 1: Filter content
event.setContent(filterProfanity(event.getContent()));
return event;
})
.thenApply(event -> {
// Step 2: Add formatting
event.setContent(addChatFormatting(event.getContent()));
return event;
})
.thenApply(event -> {
// Step 3: Log
logChatMessage(event.getSender(), event.getContent());
return event;
});
});
}
// ============================================
// From "Parallel Operations" section
// ============================================
public void parallelOperations(EventRegistry eventRegistry) {
eventRegistry.registerAsyncGlobal(PlayerChatEvent.class, future -> {
return future.thenCompose(event -> {
// Start multiple async operations in parallel
CompletableFuture<Boolean> spamCheckFuture =
CompletableFuture.supplyAsync(() -> checkForSpam(event.getContent()));
CompletableFuture<Boolean> linkCheckFuture =
CompletableFuture.supplyAsync(() -> checkForLinks(event.getContent()));
// Combine results
return spamCheckFuture.thenCombine(linkCheckFuture, (isSpam, hasLinks) -> {
if (isSpam || hasLinks) {
event.setCancelled(true);
}
return event;
});
});
});
}
// ============================================
// From "Error Handling" section
// ============================================
public void errorHandling(EventRegistry eventRegistry, HytaleLogger logger) {
eventRegistry.registerAsyncGlobal(PlayerChatEvent.class, future -> {
return future
.thenApply(event -> {
// May throw exception
riskyOperation(event);
return event;
})
.exceptionally(throwable -> {
// Handle error
logger.at(Level.SEVERE).log("Async event failed: " + throwable.getMessage());
return null; // Event will be skipped
});
});
}
// ============================================
// From "Switching to Main Thread" section
// ============================================
public void switchToMainThread(EventRegistry eventRegistry) {
eventRegistry.registerAsyncGlobal(PlayerChatEvent.class, future -> {
return future.thenApply(event -> {
// Async: Check external database
boolean isMuted = checkMuteStatus(event.getSender().getUuid());
if (isMuted) {
event.setCancelled(true);
// Schedule world thread work for player notification
World world = Universe.get().getWorld(event.getSender().getWorldUuid());
if (world != null) {
world.execute(() -> {
event.getSender().sendMessage(Message.raw("You are muted!"));
});
}
}
return event;
});
});
}
// ============================================
// From "Complete Chat Handler Example" section
// ============================================
public static class ChatPlugin extends JavaPlugin {
public ChatPlugin(JavaPluginInit init) {
super(init);
}
@Override
public void start() {
// Early priority: Content filtering
getEventRegistry().registerAsyncGlobal(
EventPriority.EARLY,
PlayerChatEvent.class,
this::filterContent
);
// Normal priority: Standard processing
getEventRegistry().registerAsyncGlobal(
PlayerChatEvent.class,
this::processChat
);
// Late priority: Logging
getEventRegistry().registerAsyncGlobal(
EventPriority.LATE,
PlayerChatEvent.class,
this::logChat
);
}
private CompletableFuture<PlayerChatEvent> filterContent(
CompletableFuture<PlayerChatEvent> future) {
return future.thenApply(event -> {
String content = event.getContent();
// Filter profanity
String filtered = filterProfanityStatic(content);
if (!filtered.equals(content)) {
event.setContent(filtered);
}
return event;
});
}
private CompletableFuture<PlayerChatEvent> processChat(
CompletableFuture<PlayerChatEvent> future) {
return future.thenApply(event -> {
if (event.isCancelled()) {
return event;
}
PlayerRef sender = event.getSender();
// Custom formatter with rank prefix
event.setFormatter((playerRef, msg) -> {
String prefix = getRankPrefix(playerRef);
return Message.raw(prefix + playerRef.getUsername() + ": " + msg);
});
return event;
});
}
private CompletableFuture<PlayerChatEvent> logChat(
CompletableFuture<PlayerChatEvent> future) {
return future.thenApply(event -> {
if (!event.isCancelled()) {
logToDatabase(
event.getSender().getUuid(),
event.getContent(),
System.currentTimeMillis()
);
}
return event;
});
}
// Stubs for methods used in the example
private static String filterProfanityStatic(String content) { return content; }
private static String getRankPrefix(PlayerRef ref) { return "[Player] "; }
private static void logToDatabase(java.util.UUID uuid, String content, long time) {}
}
// ============================================
// From "Database Integration Example" section
// ============================================
public static class ChatDatabasePlugin extends JavaPlugin {
private final ExecutorService dbExecutor = Executors.newFixedThreadPool(4);
private Database database = null; // Stub
public ChatDatabasePlugin(JavaPluginInit init) {
super(init);
}
@Override
public void start() {
getEventRegistry().registerAsyncGlobal(PlayerChatEvent.class, future -> {
return future.thenCompose(event -> {
// Run database check on dedicated thread pool
return CompletableFuture.supplyAsync(() -> {
try {
// Check if player is muted in database
boolean muted = database.isPlayerMuted(event.getSender().getUuid());
if (muted) {
event.setCancelled(true);
}
return event;
} catch (Exception e) {
getLogger().at(Level.SEVERE).log("Database error: " + e.getMessage());
return event; // Allow message on DB error
}
}, dbExecutor);
});
});
}
@Override
public void shutdown() {
dbExecutor.shutdown();
}
// Stub interface
private interface Database {
boolean isPlayerMuted(java.util.UUID uuid);
}
}
// ============================================
// From "Best Practices" safe pattern section
// ============================================
public void safePatternExample(EventRegistry eventRegistry) {
eventRegistry.registerAsyncGlobal(PlayerChatEvent.class, future -> {
return future.thenApply(event -> {
// Async work here...
String processed = processMessage(event.getContent());
event.setContent(processed);
// Safe player notification via PlayerRef
PlayerRef sender = event.getSender();
World world = Universe.get().getWorld(sender.getWorldUuid());
if (world != null) {
world.execute(() -> {
sender.sendMessage(Message.raw("Message processed"));
});
}
return event;
});
});
}
// Stub methods used in examples
private String filterContent(String content) { return content; }
private String filterProfanity(String content) { return content; }
private String addChatFormatting(String content) { return content; }
private void logChatMessage(PlayerRef sender, String content) {}
private boolean checkForSpam(String content) { return false; }
private boolean checkForLinks(String content) { return false; }
private void riskyOperation(PlayerChatEvent event) {}
private boolean checkMuteStatus(java.util.UUID uuid) { return false; }
private String processMessage(String content) { return content; }
}

View File

@@ -0,0 +1,48 @@
package org.example.docs.events;
// Example from: hytale-docs/content/events/event-system.en.md
// This file tests that ALL events documentation examples compile correctly
import com.hypixel.hytale.event.EventPriority;
import com.hypixel.hytale.event.EventRegistry;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.event.events.player.PlayerDisconnectEvent;
import com.hypixel.hytale.server.core.event.events.player.PlayerReadyEvent;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.logger.HytaleLogger;
import java.util.logging.Level;
/**
* Tests event-system.en.md documentation
*
* IMPORTANT NOTE on Event Registration:
* - Events with Void key (like PlayerConnectEvent) can use register(Class, Consumer)
* - Events with other key types (like PlayerReadyEvent with String key) must use registerGlobal(Class, Consumer)
*/
public class EventSystemExample {
public void registerEvents(EventRegistry eventRegistry, HytaleLogger logger) {
// PlayerConnectEvent has Void key, can use simple register()
eventRegistry.register(PlayerConnectEvent.class, event -> {
PlayerRef playerRef = event.getPlayerRef();
logger.at(Level.INFO).log("Player connecting: " + playerRef.getUsername());
});
// PlayerReadyEvent has String key - MUST use registerGlobal()
eventRegistry.registerGlobal(PlayerReadyEvent.class, event -> {
Player player = event.getPlayer();
logger.at(Level.INFO).log("Player ready: " + player.getDisplayName());
});
// PlayerDisconnectEvent - check its key type to determine which method to use
eventRegistry.registerGlobal(PlayerDisconnectEvent.class, event -> {
// Use event methods to access player info
});
// Register with specific priority (only works for Void key events)
eventRegistry.register(EventPriority.EARLY, PlayerConnectEvent.class, event -> {
// This runs before NORMAL priority handlers
});
}
}

View File

@@ -0,0 +1,56 @@
package org.example.docs.gettingstarted;
// Example from: hytale-docs/content/getting-started/creating-a-plugin.en.md
// This file tests that ALL code examples from the page compile correctly
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import java.util.logging.Level;
/**
* Tests creating-a-plugin.en.md documentation
*
* From "Main Plugin Class" section (lines 85-118)
*/
public class CreatingPluginExample extends JavaPlugin {
public CreatingPluginExample(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
// Called during plugin initialization
// Register configs, prepare resources
getLogger().at(Level.INFO).log("MyPlugin is setting up!");
}
@Override
public void start() {
// Called when the plugin starts
// Register commands, events, entities
getLogger().at(Level.INFO).log("MyPlugin has started!");
}
@Override
public void shutdown() {
// Called when the plugin is stopping
// Clean up resources
getLogger().at(Level.INFO).log("MyPlugin is shutting down!");
}
// ============================================
// From "Logger API" section (lines 129-138)
// ============================================
public void testLoggerAPI() {
String playerName = "TestPlayer";
// Basic logging
getLogger().at(Level.INFO).log("Information message");
getLogger().at(Level.WARNING).log("Warning message");
getLogger().at(Level.SEVERE).log("Error message");
// With formatting
getLogger().at(Level.INFO).log("Player %s joined the game", playerName);
}
}

View File

@@ -0,0 +1,91 @@
package org.example.docs.gettingstarted;
// Example from: hytale-docs/content/getting-started/plugin-lifecycle.en.md
// This file tests that ALL code examples from the page compile correctly
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.plugin.PluginState;
import com.hypixel.hytale.server.core.util.Config;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import java.util.logging.Level;
// Importing MyConfig from another example
import org.example.MyConfig;
/**
* Tests plugin-lifecycle.en.md documentation
*/
public class PluginLifecycleExample extends JavaPlugin {
private Config<MyConfig> config;
public PluginLifecycleExample(JavaPluginInit init) {
super(init);
}
// ============================================
// From "setup()" section (lines 37-45)
// ============================================
@Override
public void setup() {
// Register a configuration file
config = withConfig(MyConfig.CODEC);
getLogger().at(Level.INFO).log("Setup complete!");
}
// ============================================
// From "start()" section (lines 66-85)
// Note: Fixed getCommandRegistry().register() -> registerCommand()
// Note: Fixed PlayerRef import path
// ============================================
@Override
public void start() {
// Register commands
// getCommandRegistry().registerCommand(new MyCommand());
// Register events
getEventRegistry().register(PlayerConnectEvent.class, this::onPlayerConnect);
getLogger().at(Level.INFO).log("Plugin started!");
}
private void onPlayerConnect(PlayerConnectEvent event) {
// See PlayerRef documentation for thread-safe player references
PlayerRef playerRef = event.getPlayerRef();
getLogger().at(Level.INFO).log("Player connecting: " + playerRef.getUsername());
}
// ============================================
// From "shutdown()" section (lines 101-109)
// ============================================
@Override
public void shutdown() {
// Save any pending data
savePlayerData();
getLogger().at(Level.INFO).log("Plugin shutdown complete!");
}
private void savePlayerData() {
// Placeholder for save logic
}
// ============================================
// From "Checking Plugin State" section (lines 150-160)
// ============================================
public void testPluginState() {
if (isEnabled()) {
// Plugin is running
}
if (isDisabled()) {
// Plugin is not running
}
// Get current state
PluginState state = getState();
}
}

View File

@@ -0,0 +1,51 @@
package org.example.docs.interactions;
/**
* Example code from: hytale-docs/content/interactions/_index.en.md
* Tests the Interaction System documentation
*/
import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap;
import com.hypixel.hytale.protocol.InteractionState;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction;
public class InteractionsIndexExample {
// CORRECTED Example from documentation - Get Interaction
// Documentation was WRONG: getAsset() takes int, not String!
public void getInteractionExample() {
// Get the asset map
IndexedLookupTableAssetMap<String, Interaction> assetMap = Interaction.getAssetMap();
// Get index from string ID
int index = assetMap.getIndex("my_interaction");
// Get interaction by index
Interaction interaction = assetMap.getAsset(index);
// Or combined (but check for null!)
if (index != Integer.MIN_VALUE) {
Interaction myInteraction = assetMap.getAsset(index);
}
}
// Example from documentation - InteractionState enum
// CORRECTED order: Finished(0), Skip(1), ItemChanged(2), Failed(3), NotFinished(4)
public void interactionStateExample() {
InteractionState state = InteractionState.NotFinished;
// Check state
switch (state) {
case Finished: // value 0
break;
case Skip: // value 1
break;
case ItemChanged: // value 2
break;
case Failed: // value 3
break;
case NotFinished: // value 4
break;
}
}
}

View File

@@ -0,0 +1,88 @@
package org.example.docs.interactions;
/**
* Example code from: hytale-docs/content/interactions/selectors.en.md
* Tests the Selectors documentation
*/
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.selector.Selector;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import java.util.function.Consumer;
import java.util.function.Predicate;
public class SelectorsExample {
// Example from documentation - Select Nearby Blocks
public void selectNearbyBlocksExample(CommandBuffer<EntityStore> commandBuffer,
Ref<EntityStore> attackerRef,
double range) {
Selector.selectNearbyBlocks(
commandBuffer,
attackerRef,
range,
(x, y, z) -> {
// Process each block position
}
);
}
// Example from documentation - Select Nearby Blocks from position
public void selectNearbyBlocksFromPosition(Vector3d position, double range) {
Selector.selectNearbyBlocks(
position,
range,
(x, y, z) -> {
// Process each block position
}
);
}
// CORRECTED Example from documentation - Select Nearby Entities
// The consumer receives Ref<EntityStore> only (not BiConsumer with Vector4d!)
public void selectNearbyEntitiesExample(CommandBuffer<EntityStore> commandBuffer,
Ref<EntityStore> attackerRef,
double range) {
Selector.selectNearbyEntities(
commandBuffer,
attackerRef,
range,
(Consumer<Ref<EntityStore>>) entityRef -> {
// Process each entity
},
(Predicate<Ref<EntityStore>>) entityRef -> {
// Filter predicate - return true to include
return true;
}
);
}
// Example from documentation - Select Nearby Entities from position with ComponentAccessor
public void selectNearbyEntitiesFromPosition(ComponentAccessor<EntityStore> componentAccessor,
Vector3d position,
double range) {
Selector.selectNearbyEntities(
componentAccessor,
position,
range,
entityRef -> {
// Process each entity
},
entityRef -> true // Filter or null
);
}
// CORRECTED Example from documentation - Entity Validity Check
// Documentation was wrong: entityRef.get().isValid() should be entityRef.isValid()
public void entityValidityExample(Ref<EntityStore> entityRef) {
// Correct way to check validity
if (entityRef.isValid()) {
// Safe to access entity components
// ... perform operations
}
}
}

View File

@@ -0,0 +1,499 @@
package org.example.docs.inventory;
// Example from: hytale-docs/content/inventory/containers.en.md
// Tests the Containers documentation examples
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.inventory.Inventory;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer;
import com.hypixel.hytale.server.core.inventory.container.SortType;
import com.hypixel.hytale.server.core.inventory.MaterialQuantity;
import com.hypixel.hytale.server.core.inventory.ResourceQuantity;
import com.hypixel.hytale.server.core.inventory.transaction.ItemStackTransaction;
import com.hypixel.hytale.server.core.inventory.transaction.ItemStackSlotTransaction;
import com.hypixel.hytale.server.core.inventory.transaction.SlotTransaction;
import com.hypixel.hytale.server.core.inventory.transaction.MoveTransaction;
import com.hypixel.hytale.server.core.inventory.transaction.ClearTransaction;
import com.hypixel.hytale.server.core.inventory.transaction.ListTransaction;
import com.hypixel.hytale.server.core.inventory.transaction.MaterialTransaction;
import com.hypixel.hytale.server.core.inventory.transaction.ResourceTransaction;
import com.hypixel.hytale.server.core.inventory.transaction.TagTransaction;
import com.hypixel.hytale.event.EventPriority;
import com.hypixel.hytale.protocol.SmartMoveType;
import java.util.List;
import java.util.logging.Level;
/**
* Tests Containers documentation
*/
public class ContainersExample extends JavaPlugin {
public ContainersExample(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
// All testing done in methods below
}
// =============================================
// Inventory Structure - CORRECT
// =============================================
public void inventoryStructure(Player player) {
Inventory inv = player.getInventory();
// Get individual sections
ItemContainer storage = inv.getStorage(); // Main storage (36 slots default)
ItemContainer hotbar = inv.getHotbar(); // Hotbar (9 slots default)
ItemContainer armor = inv.getArmor(); // Armor slots
ItemContainer utility = inv.getUtility(); // Utility items (4 slots default)
ItemContainer backpack = inv.getBackpack(); // Backpack (expandable)
// Combined containers for operations across multiple sections
CombinedItemContainer hotbarFirst = inv.getCombinedHotbarFirst();
CombinedItemContainer storageFirst = inv.getCombinedStorageFirst();
}
// =============================================
// ItemContainer Basics - Reading Items - CORRECT
// =============================================
public void readingItems(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
// Get capacity (NOT getSize!)
short capacity = container.getCapacity();
// Get item at slot (slot is SHORT type)
ItemStack item = container.getItemStack((short) 0);
// Check if empty
boolean empty = container.isEmpty();
// Safe item access
ItemStack stack = container.getItemStack((short) 0);
if (!ItemStack.isEmpty(stack)) {
String itemId = stack.getItemId();
int quantity = stack.getQuantity();
}
}
// =============================================
// Setting Items - CORRECT
// =============================================
public void settingItems(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getHotbar();
// Set item at slot - returns a Transaction
ItemStackSlotTransaction transaction = container.setItemStackForSlot(
(short) 0,
new ItemStack("iron_sword", 1)
);
// Check if operation succeeded
if (transaction.succeeded()) {
// Item was set successfully
}
// Clear a slot
container.setItemStackForSlot((short) 0, null);
}
// =============================================
// Adding Items - CORRECT
// =============================================
public void addingItems(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
ItemStack toAdd = new ItemStack("wood_plank", 64);
// Add to first available slot (stacks with existing items first)
ItemStackTransaction transaction = container.addItemStack(toAdd);
// Check for remaining items that couldn't fit
ItemStack remainder = transaction.getRemainder();
if (!ItemStack.isEmpty(remainder)) {
// Some items couldn't fit
int leftover = remainder.getQuantity();
}
// Add to specific slot
ItemStackSlotTransaction slotTransaction = container.addItemStackToSlot(
(short) 5,
toAdd
);
// Check if container can fit items before adding
if (container.canAddItemStack(toAdd)) {
container.addItemStack(toAdd);
}
}
// =============================================
// Removing Items - CORRECT
// =============================================
public void removingItems(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
// Remove from specific slot
SlotTransaction transaction = container.removeItemStackFromSlot((short) 0);
// Remove specific quantity from slot
ItemStackSlotTransaction removeTransaction = container.removeItemStackFromSlot(
(short) 0,
10 // quantity to remove
);
// Remove specific item type from anywhere in container
ItemStack toRemove = new ItemStack("iron_ore", 5);
ItemStackTransaction itemTransaction = container.removeItemStack(toRemove);
// Check if items can be removed before removing
if (container.canRemoveItemStack(toRemove)) {
container.removeItemStack(toRemove);
}
}
// =============================================
// Active Slots - CORRECT
// =============================================
public void activeSlots(Player player) {
Inventory inv = player.getInventory();
// Get active hotbar slot (0-8)
byte activeHotbar = inv.getActiveHotbarSlot();
// Set active hotbar slot
inv.setActiveHotbarSlot((byte) 3);
// Get item currently in hand
ItemStack handItem = inv.getItemInHand();
// Get/set active utility slot
byte activeUtility = inv.getActiveUtilitySlot();
inv.setActiveUtilitySlot((byte) 1);
// Get utility item
ItemStack utilityItem = inv.getUtilityItem();
}
// =============================================
// Moving Items - CORRECT
// =============================================
public void movingItems(Player player) {
Inventory inv = player.getInventory();
ItemContainer storage = inv.getStorage();
ItemContainer hotbar = inv.getHotbar();
// Move item from one slot to another in same container
MoveTransaction<SlotTransaction> transaction = storage.moveItemStackFromSlotToSlot(
(short) 0, // from slot
64, // quantity
storage, // to container
(short) 5 // to slot
);
// Move between different containers
storage.moveItemStackFromSlotToSlot(
(short) 0,
32,
hotbar,
(short) 0
);
// Use Inventory's moveItem for section-based moves
inv.moveItem(
Inventory.STORAGE_SECTION_ID, // from section
5, // from slot
10, // quantity
Inventory.HOTBAR_SECTION_ID, // to section
0 // to slot
);
}
// =============================================
// Smart Moving - CORRECT
// =============================================
public void smartMoving(Player player) {
Inventory inv = player.getInventory();
// Smart move considers item type for optimal placement
inv.smartMoveItem(
Inventory.STORAGE_SECTION_ID,
0, // slot
64, // quantity
SmartMoveType.EquipOrMergeStack // tries to equip armor or merge stacks
);
}
// =============================================
// Iterating Containers - CORRECT
// =============================================
public void iteratingContainers(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
// Iterate all non-empty slots
container.forEach((slot, itemStack) -> {
getLogger().at(Level.INFO).log("Slot " + slot + ": " +
itemStack.getItemId() + " x" + itemStack.getQuantity());
});
// Count items matching a condition
int swordCount = container.countItemStacks(
stack -> stack.getItemId().contains("sword")
);
// Check if container has stackable items
ItemStack testStack = new ItemStack("stone", 1);
boolean hasStackable = container.containsItemStacksStackableWith(testStack);
}
// =============================================
// Checking Contents - CORRECT
// =============================================
public boolean hasItems(ItemContainer container, String itemId, int amount) {
int count = container.countItemStacks(
stack -> stack.getItemId().equals(itemId)
);
return count >= amount;
}
public short findSlotWithItem(ItemContainer container, String itemId) {
for (short i = 0; i < container.getCapacity(); i++) {
ItemStack stack = container.getItemStack(i);
if (!ItemStack.isEmpty(stack) && stack.getItemId().equals(itemId)) {
return i;
}
}
return -1; // Not found
}
public int countEmptySlots(ItemContainer container) {
int empty = 0;
for (short i = 0; i < container.getCapacity(); i++) {
if (ItemStack.isEmpty(container.getItemStack(i))) {
empty++;
}
}
return empty;
}
// =============================================
// Clearing Containers - CORRECT
// =============================================
public void clearingContainers(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
// Clear entire container
ClearTransaction transaction = container.clear();
// Drop all items (returns list of dropped items)
List<ItemStack> droppedItems = container.dropAllItemStacks();
// Remove all items (returns list)
List<ItemStack> removedItems = container.removeAllItemStacks();
// Clear entire player inventory
inv.clear();
// Drop all from player inventory
List<ItemStack> allDropped = inv.dropAllItemStacks();
}
// =============================================
// Sorting - CORRECT
// Available SortType values: NAME, TYPE, RARITY (no QUANTITY!)
// =============================================
public void sorting(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
// Sort items
container.sortItems(SortType.NAME);
// Sort via Inventory (also saves sort preference)
inv.sortStorage(SortType.NAME);
inv.setSortType(SortType.RARITY); // Or TYPE
}
// =============================================
// Container Events - CORRECT
// =============================================
public void containerEvents(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
// Register for container change events
container.registerChangeEvent(event -> {
ItemContainer changedContainer = event.container();
// Transaction transaction = event.transaction(); // Possible name collision
getLogger().at(Level.INFO).log("Container changed!");
});
// With priority
container.registerChangeEvent(EventPriority.EARLY, event -> {
// Handle early
});
}
// =============================================
// Bulk Operations - CORRECT
// =============================================
public void bulkOperations(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
// Add multiple items
List<ItemStack> items = List.of(
new ItemStack("iron_ore", 10),
new ItemStack("gold_ore", 5)
);
if (container.canAddItemStacks(items)) {
ListTransaction<ItemStackTransaction> transaction = container.addItemStacks(items);
}
// Remove multiple items
if (container.canRemoveItemStacks(items)) {
container.removeItemStacks(items);
}
// Add items in order (preserves slot positions)
container.addItemStacksOrdered(items);
container.addItemStacksOrdered((short) 5, items); // Starting at slot 5
}
// =============================================
// Material and Resource Removal - CORRECT
// MaterialQuantity constructor: (itemId, resourceTypeId, tag, quantity, metadata)
// At least one of itemId, resourceTypeId, or tag must be non-null
// =============================================
public void materialResourceRemoval(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
// Remove by material using itemId
MaterialQuantity materialByItem = new MaterialQuantity("iron_ingot", null, null, 5, null);
if (container.canRemoveMaterial(materialByItem)) {
MaterialTransaction transaction = container.removeMaterial(materialByItem);
}
// Remove by material using resourceTypeId
MaterialQuantity materialByResource = new MaterialQuantity(null, "iron", null, 5, null);
if (container.canRemoveMaterial(materialByResource)) {
MaterialTransaction transaction = container.removeMaterial(materialByResource);
}
// Remove by resource type (simpler constructor)
ResourceQuantity resource = new ResourceQuantity("wood", 10);
if (container.canRemoveResource(resource)) {
ResourceTransaction transaction = container.removeResource(resource);
}
// Remove by tag index
int tagIndex = 0;
int quantity = 5;
if (container.canRemoveTag(tagIndex, quantity)) {
TagTransaction transaction = container.removeTag(tagIndex, quantity);
}
// Bulk removal
List<MaterialQuantity> materials = List.of(
new MaterialQuantity("iron_ingot", null, null, 2, null),
new MaterialQuantity(null, null, "Wood", 5, null) // Using tag
);
if (container.canRemoveMaterials(materials)) {
container.removeMaterials(materials);
}
}
// =============================================
// Advanced Move Operations - CORRECT
// =============================================
public void advancedMoveOperations(Player player) {
Inventory inv = player.getInventory();
ItemContainer storage = inv.getStorage();
ItemContainer hotbar = inv.getHotbar();
ItemContainer backpack = inv.getBackpack();
// Move all items to another container
ListTransaction<MoveTransaction<ItemStackTransaction>> result =
storage.moveAllItemStacksTo(hotbar, backpack);
// Move all items matching a condition
storage.moveAllItemStacksTo(
item -> item.getItemId().contains("ore"),
hotbar
);
// Quick stack: only moves items that can stack with existing items
storage.quickStackTo(hotbar);
// Combine small stacks into one slot
storage.combineItemStacksIntoSlot(storage, (short) 0);
// Swap items between containers
storage.swapItems(
(short) 0, // source position
hotbar, // target container
(short) 0, // destination position
(short) 5 // number of slots to swap
);
}
// =============================================
// Replace Operations - CORRECT
// =============================================
public void replaceOperations(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
ItemStack expectedItem = new ItemStack("iron_sword", 1);
ItemStack newItem = new ItemStack("diamond_sword", 1);
// Replace item in slot if it matches expected
ItemStackSlotTransaction transaction = container.replaceItemStackInSlot(
(short) 0,
expectedItem, // must match current item to proceed
newItem
);
// Replace all items using a function
container.replaceAll((slot, existing) -> {
if (existing.getItemId().equals("old_item")) {
return new ItemStack("new_item", existing.getQuantity());
}
return existing;
});
}
// =============================================
// Safe pattern for working with containers - CORRECT
// =============================================
public void safeAddItem(ItemContainer container, ItemStack item) {
if (ItemStack.isEmpty(item)) {
return;
}
if (!container.canAddItemStack(item)) {
// Handle full container
return;
}
ItemStackTransaction transaction = container.addItemStack(item);
if (!transaction.succeeded()) {
// Handle failure
}
}
}

View File

@@ -0,0 +1,212 @@
package org.example.docs.inventory;
// Example from: hytale-docs/content/inventory/_index.en.md and itemstacks.en.md
// Tests the inventory documentation examples
//
// FINDINGS:
// - player.getItemInHand() does NOT exist - use player.getInventory().getItemInHand()
// - item.getName() does NOT exist on Item - use item.getId() or stack.getItemId()
// - getQuantity() is correct (not getCount())
// - withQuantity(0) returns null
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.inventory.Inventory;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import java.util.logging.Level;
/**
* Tests inventory documentation
*/
public class InventoryExample extends JavaPlugin {
public InventoryExample(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef playerRef = event.getPlayerRef();
Player player = event.getPlayer();
if (player != null) {
// =============================================
// Getting Inventory - CORRECT
// =============================================
Inventory inv = player.getInventory();
// =============================================
// Getting Item in Hand - CORRECT pattern
// Note: player.getItemInHand() does NOT exist!
// Must use inventory method
// =============================================
ItemStack hand = inv.getItemInHand();
// Check if item exists
if (hand != null && !hand.isEmpty()) {
// Item.getName() does NOT exist!
// Use getId() or getItemId()
String itemId = hand.getItemId();
getLogger().at(Level.INFO).log("Holding: " + itemId);
// Or via Item asset
Item item = hand.getItem();
String id = item.getId();
}
}
});
}
// =============================================
// ItemStack Creation - CORRECT
// =============================================
public void itemStackCreation() {
// Create by item ID
ItemStack sword = new ItemStack("iron_sword");
// Create with quantity
ItemStack materials = new ItemStack("wood_plank", 64);
// Create with full parameters
ItemStack damagedSword = new ItemStack("iron_sword", 1, 50.0, 100.0, null);
}
// =============================================
// ItemStack Properties - CORRECT
// =============================================
public void itemStackProperties() {
ItemStack stack = new ItemStack("iron_sword", 1);
// Get the item ID
String itemId = stack.getItemId();
// Get the Item asset
Item item = stack.getItem();
// Get quantity (NOT getCount!)
int quantity = stack.getQuantity();
// Check if empty
boolean empty = stack.isEmpty();
// Check validity
boolean valid = stack.isValid();
// Durability
double durability = stack.getDurability();
double maxDurability = stack.getMaxDurability();
boolean unbreakable = stack.isUnbreakable();
boolean broken = stack.isBroken();
}
// =============================================
// Modifying ItemStacks - CORRECT (immutable pattern)
// =============================================
public void modifyingItemStacks() {
ItemStack stack = new ItemStack("iron_sword", 1);
// Change quantity - returns NEW ItemStack or null if quantity is 0
ItemStack moreItems = stack.withQuantity(32);
// Change durability
ItemStack damaged = stack.withDurability(50.0);
// Increase durability
ItemStack repaired = stack.withIncreasedDurability(25.0);
// Restore full durability
ItemStack fullyRepaired = stack.withRestoredDurability(100.0);
// Change max durability
ItemStack stronger = stack.withMaxDurability(200.0);
// Change state
ItemStack newState = stack.withState("activated");
}
// =============================================
// EMPTY constant - CORRECT
// =============================================
public void emptyStack() {
// Static empty instance (singleton)
ItemStack empty = ItemStack.EMPTY;
// Static helper method (handles null and empty)
ItemStack stack = new ItemStack("iron_sword", 1);
if (ItemStack.isEmpty(stack)) {
// Handles null and empty stacks
}
}
// =============================================
// Comparing ItemStacks - CORRECT
// =============================================
public void comparingItemStacks() {
ItemStack a = new ItemStack("iron_sword", 1);
ItemStack b = new ItemStack("iron_sword", 5);
ItemStack c = new ItemStack("diamond_sword", 1);
// Check if stackable
boolean canStack = a.isStackableWith(b);
// Check equivalent type
boolean sameType = a.isEquivalentType(b);
// Check same item type only
boolean sameItem = ItemStack.isSameItemType(a, c);
// Static helpers (handle nulls safely)
ItemStack.isStackableWith(a, b);
ItemStack.isEquivalentType(a, b);
}
// =============================================
// Working with Metadata - CORRECT
// =============================================
public void metadataExample() {
ItemStack stack = new ItemStack("iron_sword", 1);
// Create metadata codec
KeyedCodec<String> OWNER_KEY = new KeyedCodec<>("Owner", Codec.STRING);
// Add metadata
ItemStack withOwner = stack.withMetadata(OWNER_KEY, "PlayerName");
// Read metadata
String owner = withOwner.getFromMetadataOrNull(OWNER_KEY);
// Read with key and codec
Integer level = stack.getFromMetadataOrNull("Level", Codec.INTEGER);
}
// =============================================
// Common Patterns - CORRECT
// =============================================
public ItemStack consumeOne(ItemStack stack) {
if (stack == null || stack.isEmpty()) {
return null;
}
int newQuantity = stack.getQuantity() - 1;
// withQuantity returns null if quantity is 0
return stack.withQuantity(newQuantity);
}
public void useItem(ItemContainer container, short slot) {
ItemStack current = container.getItemStack(slot);
ItemStack remaining = consumeOne(current);
// remaining may be null if stack was depleted
container.setItemStackForSlot(slot, remaining);
}
}

View File

@@ -0,0 +1,289 @@
package org.example.docs.inventory;
// Example from: hytale-docs/content/inventory/itemstacks.en.md
// Tests the ItemStacks documentation examples
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import org.bson.BsonDocument;
/**
* Tests ItemStacks documentation
*/
public class ItemStacksExample extends JavaPlugin {
public ItemStacksExample(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
// All testing done in methods below
}
// =============================================
// Creating ItemStacks - CORRECT
// =============================================
public void creatingItemStacks() {
// Create by item ID
ItemStack sword = new ItemStack("iron_sword");
// Create with quantity
ItemStack materials = new ItemStack("wood_plank", 64);
// Create with quantity and metadata
BsonDocument metadata = new BsonDocument();
ItemStack customItem = new ItemStack("iron_sword", 1, metadata);
// Create with full parameters (durability)
ItemStack damagedSword = new ItemStack("iron_sword", 1, 50.0, 100.0, null);
// Parameters: itemId, quantity, durability, maxDurability, metadata
}
// =============================================
// ItemStack Properties - CORRECT
// =============================================
public void itemStackProperties() {
ItemStack stack = new ItemStack("iron_sword", 1);
// Get the item ID
String itemId = stack.getItemId(); // "iron_sword"
// Get the Item asset
Item item = stack.getItem();
// Get quantity (NOT getCount!)
int quantity = stack.getQuantity();
// Check if empty
boolean empty = stack.isEmpty();
// Check validity
boolean valid = stack.isValid();
// Durability
double durability = stack.getDurability();
double maxDurability = stack.getMaxDurability();
boolean unbreakable = stack.isUnbreakable(); // true if maxDurability <= 0
boolean broken = stack.isBroken(); // true if durability == 0
}
// =============================================
// Modifying ItemStacks - CORRECT (immutable pattern)
// =============================================
public void modifyingItemStacks() {
ItemStack stack = new ItemStack("iron_sword", 1);
// Change quantity - returns NEW ItemStack or null if quantity is 0
ItemStack moreItems = stack.withQuantity(32);
// Change durability
ItemStack damaged = stack.withDurability(50.0);
// Increase durability
ItemStack repaired = stack.withIncreasedDurability(25.0);
// Restore full durability
ItemStack fullyRepaired = stack.withRestoredDurability(100.0);
// Change max durability
ItemStack stronger = stack.withMaxDurability(200.0);
// Change state (for items with states)
// ItemStack newState = stack.withState("activated");
// Add/modify metadata
BsonDocument metadataDocument = new BsonDocument();
ItemStack withMeta = stack.withMetadata(metadataDocument);
// Add specific metadata value
ItemStack tagged = stack.withMetadata("CustomKey", Codec.STRING, "CustomValue");
}
// =============================================
// The EMPTY Constant - CORRECT
// =============================================
public void emptyConstant() {
// Static empty instance (singleton)
ItemStack empty = ItemStack.EMPTY;
ItemStack stack = new ItemStack("iron_sword", 1);
// Check for empty
if (stack.isEmpty()) {
// Stack is empty
}
// Static helper method
if (ItemStack.isEmpty(stack)) {
// Handles null and empty stacks
}
}
// =============================================
// Comparing ItemStacks - CORRECT
// =============================================
public void comparingItemStacks() {
ItemStack a = new ItemStack("iron_sword", 1);
ItemStack b = new ItemStack("iron_sword", 5);
ItemStack c = new ItemStack("diamond_sword", 1);
// Check if stackable (same itemId, durability, maxDurability, AND metadata)
// Note: Different quantities can stack, but durability values must match exactly
boolean canStack = a.isStackableWith(b);
// Check equivalent type (same itemId and metadata, ignores durability values)
boolean sameType = a.isEquivalentType(b);
// Check same item type only (just itemId comparison)
boolean sameItem = ItemStack.isSameItemType(a, c); // false
// Static helpers (handle nulls safely)
ItemStack.isStackableWith(a, b);
ItemStack.isEquivalentType(a, b);
}
// =============================================
// Working with Metadata - CORRECT
// =============================================
public void metadataExample() {
ItemStack stack = new ItemStack("iron_sword", 1);
// Create metadata codec
KeyedCodec<String> OWNER_KEY = new KeyedCodec<>("Owner", Codec.STRING);
// Add metadata
ItemStack withOwner = stack.withMetadata(OWNER_KEY, "PlayerName");
// Read metadata
String owner = withOwner.getFromMetadataOrNull(OWNER_KEY);
// Read with key and codec
Integer level = stack.getFromMetadataOrNull("Level", Codec.INTEGER);
}
// =============================================
// Block Items - CORRECT
// =============================================
public void blockItems() {
ItemStack stack = new ItemStack("stone", 1);
// Get associated block key (null if not a block item)
String blockKey = stack.getBlockKey();
if (blockKey != null) {
// This item can be placed as a block
}
// Check via Item asset
Item item = stack.getItem();
if (item.hasBlockType()) {
String blockId = item.getBlockId();
}
}
// =============================================
// Common Patterns - CORRECT
// =============================================
// Consuming Items
public ItemStack consumeOne(ItemStack stack) {
if (stack == null || stack.isEmpty()) {
return null;
}
int newQuantity = stack.getQuantity() - 1;
// withQuantity returns null if quantity is 0
return stack.withQuantity(newQuantity);
}
// Usage with container
public void useItem(ItemContainer container, short slot) {
ItemStack current = container.getItemStack(slot);
ItemStack remaining = consumeOne(current);
// remaining may be null if stack was depleted
container.setItemStackForSlot(slot, remaining);
}
// Checking Item Type
public boolean isHoldingSword(ItemStack hand) {
if (hand == null || hand.isEmpty()) {
return false;
}
// Check item type by ID
return hand.getItemId().contains("sword");
// Or check via Item asset
// return hand.getItem().getCategory().equals("weapon");
}
// Splitting Stacks
public ItemStack[] splitStack(ItemStack stack, int splitAmount) {
if (stack == null || stack.isEmpty()) {
return null;
}
int currentQuantity = stack.getQuantity();
if (splitAmount >= currentQuantity) {
return new ItemStack[] { stack, null };
}
// Create two stacks
ItemStack remaining = stack.withQuantity(currentQuantity - splitAmount);
ItemStack split = stack.withQuantity(splitAmount);
return new ItemStack[] { remaining, split };
}
// Merging Stacks
public ItemStack[] mergeStacks(ItemStack target, ItemStack source) {
if (!target.isStackableWith(source)) {
return new ItemStack[] { target, source }; // Can't merge
}
int maxStack = target.getItem().getMaxStack();
int totalQuantity = target.getQuantity() + source.getQuantity();
if (totalQuantity <= maxStack) {
// Full merge
return new ItemStack[] {
target.withQuantity(totalQuantity),
null
};
}
// Partial merge
return new ItemStack[] {
target.withQuantity(maxStack),
source.withQuantity(totalQuantity - maxStack)
};
}
// =============================================
// Best Practices - CORRECT
// =============================================
public void bestPractices() {
ItemStack stack = new ItemStack("iron_sword", 1);
int newQuantity = 10;
// Good: Handle null from withQuantity
ItemStack result = stack.withQuantity(newQuantity);
if (result == null) {
// Stack depleted, handle appropriately
}
// Good: Safe empty check
if (ItemStack.isEmpty(stack)) {
// Handles both null and empty ItemStack.EMPTY
}
// Bad: Ignoring the returned value
stack.withQuantity(10); // Returns new stack, original unchanged!
}
}

View File

@@ -0,0 +1,362 @@
package org.example.docs.inventory;
// Example from: hytale-docs/content/inventory/transactions.en.md
// Tests the Transactions documentation examples
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.inventory.Inventory;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
import com.hypixel.hytale.server.core.inventory.transaction.Transaction;
import com.hypixel.hytale.server.core.inventory.transaction.SlotTransaction;
import com.hypixel.hytale.server.core.inventory.transaction.ItemStackSlotTransaction;
import com.hypixel.hytale.server.core.inventory.transaction.ItemStackTransaction;
import com.hypixel.hytale.server.core.inventory.transaction.MoveTransaction;
import com.hypixel.hytale.server.core.inventory.transaction.ListTransaction;
import com.hypixel.hytale.server.core.inventory.transaction.ActionType;
import com.hypixel.hytale.server.core.inventory.transaction.MoveType;
import com.hypixel.hytale.server.core.Message;
import java.util.List;
import java.util.logging.Level;
/**
* Tests Transactions documentation
*/
public class TransactionsExample extends JavaPlugin {
public TransactionsExample(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
// All testing done in methods below
}
// =============================================
// SlotTransaction - CORRECT
// =============================================
public void slotTransactionExample(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
SlotTransaction transaction = container.removeItemStackFromSlot((short) 0);
if (transaction.succeeded()) {
// What was in the slot before
ItemStack before = transaction.getSlotBefore();
// What is in the slot now
ItemStack after = transaction.getSlotAfter();
// The item that was removed/output
ItemStack output = transaction.getOutput();
// Which slot was affected
short slot = transaction.getSlot();
// What type of action (ADD, REMOVE, REPLACE)
ActionType action = transaction.getAction();
}
}
// =============================================
// ItemStackSlotTransaction - CORRECT
// =============================================
public void itemStackSlotTransactionExample(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
ItemStackSlotTransaction transaction = container.setItemStackForSlot(
(short) 0,
new ItemStack("iron_sword", 1)
);
if (transaction.succeeded()) {
short slot = transaction.getSlot();
ItemStack before = transaction.getSlotBefore();
ItemStack after = transaction.getSlotAfter();
// Check options used
boolean wasFiltered = transaction.isFilter();
boolean wasAllOrNothing = transaction.isAllOrNothing();
}
}
// =============================================
// ItemStackTransaction - CORRECT
// =============================================
public void itemStackTransactionExample(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
ItemStack toAdd = new ItemStack("stone", 128);
ItemStackTransaction transaction = container.addItemStack(toAdd);
if (transaction.succeeded()) {
// Items that couldn't fit (null if all fit)
ItemStack remainder = transaction.getRemainder();
// Original item we tried to add
ItemStack query = transaction.getQuery();
// List of all slot transactions that occurred
List<ItemStackSlotTransaction> slotTransactions = transaction.getSlotTransactions();
for (ItemStackSlotTransaction slotTx : slotTransactions) {
getLogger().at(Level.INFO).log("Modified slot " + slotTx.getSlot());
}
}
}
// =============================================
// MoveTransaction - CORRECT
// =============================================
public void moveTransactionExample(Player player) {
Inventory inv = player.getInventory();
ItemContainer storage = inv.getStorage();
ItemContainer hotbar = inv.getHotbar();
MoveTransaction<SlotTransaction> transaction = storage.moveItemStackFromSlotToSlot(
(short) 0,
32,
hotbar,
(short) 0
);
if (transaction.succeeded()) {
// Transaction for removing from source
SlotTransaction removeTransaction = transaction.getRemoveTransaction();
// Transaction for adding to destination
SlotTransaction addTransaction = transaction.getAddTransaction();
// The destination container
ItemContainer destination = transaction.getOtherContainer();
// Direction of the move
MoveType moveType = transaction.getMoveType();
}
}
// =============================================
// ListTransaction - CORRECT
// =============================================
public void listTransactionExample(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
List<ItemStack> items = List.of(
new ItemStack("stone", 64),
new ItemStack("wood", 64)
);
ListTransaction<ItemStackTransaction> transaction = container.addItemStacks(items);
if (transaction.succeeded()) {
List<ItemStackTransaction> results = transaction.getList();
for (ItemStackTransaction result : results) {
if (result.succeeded()) {
ItemStack remainder = result.getRemainder();
// ...
}
}
}
}
// =============================================
// ActionType - CORRECT
// =============================================
public void actionTypeExample(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
SlotTransaction transaction = container.removeItemStackFromSlot((short) 0);
if (transaction.succeeded()) {
ActionType action = transaction.getAction();
// Check action characteristics
if (action.isAdd()) { /* operation adds items */ }
if (action.isRemove()) { /* operation removes items */ }
if (action.isDestroy()) { /* operation destroys slot contents */ }
}
}
// =============================================
// MoveType - CORRECT
// =============================================
public void moveTypeExample(Player player) {
Inventory inv = player.getInventory();
ItemContainer storage = inv.getStorage();
ItemContainer hotbar = inv.getHotbar();
MoveTransaction<SlotTransaction> transaction = storage.moveItemStackFromSlotToSlot(
(short) 0,
32,
hotbar,
(short) 0
);
if (transaction.succeeded()) {
MoveType moveType = transaction.getMoveType();
// MOVE_TO_SELF: Items being moved TO this container
// MOVE_FROM_SELF: Items being moved FROM this container
}
}
// =============================================
// Common Patterns - Check Before Modify - CORRECT
// =============================================
public boolean safeTransfer(ItemContainer from, ItemContainer to, String itemId, int amount) {
ItemStack toRemove = new ItemStack(itemId, amount);
// Check both operations can succeed
if (!from.canRemoveItemStack(toRemove)) {
return false; // Not enough items
}
if (!to.canAddItemStack(toRemove)) {
return false; // No space
}
// Execute removal
ItemStackTransaction removeResult = from.removeItemStack(toRemove);
if (!removeResult.succeeded()) {
return false;
}
// Execute addition
ItemStack removed = removeResult.getQuery();
ItemStackTransaction addResult = to.addItemStack(removed);
// Handle any remainder
ItemStack remainder = addResult.getRemainder();
if (!ItemStack.isEmpty(remainder)) {
// Put remainder back
from.addItemStack(remainder);
}
return true;
}
// =============================================
// Handling Remainders - CORRECT
// =============================================
public void giveItemSafe(Player player, String itemId, int quantity) {
ItemContainer storage = player.getInventory().getStorage();
ItemStack item = new ItemStack(itemId, quantity);
ItemStackTransaction result = storage.addItemStack(item);
if (!result.succeeded()) {
player.sendMessage(Message.raw("Inventory full!"));
return;
}
ItemStack remainder = result.getRemainder();
if (!ItemStack.isEmpty(remainder)) {
player.sendMessage(Message.raw(
"Only received " + (quantity - remainder.getQuantity()) + " items, " +
"inventory full!"
));
}
}
// =============================================
// Atomic Operations - CORRECT
// =============================================
public boolean buyItem(Player player, String itemId, int price, int quantity) {
Inventory inv = player.getInventory();
ItemContainer storage = inv.getStorage();
ItemStack currency = new ItemStack("gold_coin", price);
ItemStack item = new ItemStack(itemId, quantity);
// Check both operations can succeed fully
if (!storage.canRemoveItemStack(currency)) {
player.sendMessage(Message.raw("Not enough gold!"));
return false;
}
if (!storage.canAddItemStack(item)) {
player.sendMessage(Message.raw("Inventory full!"));
return false;
}
// Remove currency with allOrNothing=true
ItemStackTransaction removeResult = storage.removeItemStack(currency, true, true);
if (!removeResult.succeeded()) {
return false;
}
// Add item
ItemStackTransaction addResult = storage.addItemStack(item, true, false, true);
if (!addResult.succeeded()) {
// Rollback: return the currency
storage.addItemStack(currency);
return false;
}
return true;
}
// =============================================
// Tracking Changes - CORRECT
// =============================================
public void onContainerChange(ItemContainer.ItemContainerChangeEvent event) {
Transaction transaction = event.transaction();
// Check if hotbar slot 0 was affected
if (transaction.wasSlotModified((short) 0)) {
getLogger().at(Level.INFO).log("First slot was modified!");
}
// Check all hotbar slots
for (short i = 0; i < 9; i++) {
if (transaction.wasSlotModified(i)) {
getLogger().at(Level.INFO).log("Hotbar slot " + i + " modified");
}
}
}
// =============================================
// Transaction Options - CORRECT
// =============================================
public void transactionOptions(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
ItemStack item = new ItemStack("stone", 64);
// Default behavior
container.addItemStack(item);
// With options
container.addItemStack(item, true, false, true); // allOrNothing, fullStacks, filter
container.removeItemStack(item, true, true); // allOrNothing, filter
}
// =============================================
// Best Practices - CORRECT
// =============================================
public void bestPractices(Player player) {
Inventory inv = player.getInventory();
ItemContainer container = inv.getStorage();
ItemStack item = new ItemStack("stone", 64);
// Good: Check success and handle remainder
ItemStackTransaction tx = container.addItemStack(item);
if (tx.succeeded()) {
ItemStack remainder = tx.getRemainder();
if (!ItemStack.isEmpty(remainder)) {
// Handle leftover items
}
}
// Bad: Assume success
container.addItemStack(item); // Might fail silently!
}
}

View File

@@ -0,0 +1,301 @@
package org.example.docs.permissions;
// Example from: hytale-docs/content/permissions/_index.en.md
// Tests the Permissions documentation examples
//
// FINDINGS:
// - HytaleServer.get().getPermissionsModule() does NOT exist - use PermissionsModule.get()
// - PlayerGroupEvent.isAdded() does NOT exist - use PlayerGroupEvent.Added or PlayerGroupEvent.Removed
// - PlayerGroupEvent.getGroup() does NOT exist - use getGroupName()
// - GroupPermissionChangeEvent uses separate classes: Added and Removed
// - PermissionProvider interface does NOT have hasPermission(UUID, String)
// - The interface has: getUserPermissions(), getGroupPermissions(), getGroupsForUser(), etc.
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.permissions.PermissionsModule;
import com.hypixel.hytale.server.core.permissions.provider.PermissionProvider;
import com.hypixel.hytale.server.core.event.events.permissions.PlayerGroupEvent;
import com.hypixel.hytale.server.core.event.events.permissions.GroupPermissionChangeEvent;
import com.hypixel.hytale.server.core.event.events.permissions.PlayerPermissionChangeEvent;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import java.util.Set;
import java.util.UUID;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import javax.annotation.Nonnull;
/**
* Tests Permissions documentation
*/
public class PermissionsExample extends JavaPlugin {
public PermissionsExample(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
// Register permission events
registerEvents();
}
@Override
protected void start() {
// Add custom permission provider
PermissionsModule permissions = PermissionsModule.get();
permissions.addProvider(new VIPPermissionProvider());
}
// =============================================
// Accessing Permissions Module - CORRECT
// Note: HytaleServer.get().getPermissionsModule() does NOT exist!
// =============================================
public void accessPermissionsModule() {
// CORRECT way to access
PermissionsModule permissions = PermissionsModule.get();
// WRONG - this method does not exist:
// PermissionsModule permissions = HytaleServer.get().getPermissionsModule();
}
// =============================================
// Checking Permissions - CORRECT
// =============================================
public void checkingPermissions(Player player, CommandContext ctx) {
PermissionsModule permissions = PermissionsModule.get();
UUID playerUuid = player.getPlayerRef().getUuid();
// Check if player has permission
boolean canEdit = permissions.hasPermission(playerUuid, "hytale.editor.asset");
// Check with default value
boolean canBuild = permissions.hasPermission(playerUuid, "myplugin.build", false);
// Check in command context
if (permissions.hasPermission(ctx.sender().getUuid(), "myplugin.admin")) {
// Admin-only logic
}
}
// =============================================
// User Permissions - CORRECT
// =============================================
public void userPermissions(Player player) {
PermissionsModule permissions = PermissionsModule.get();
UUID playerUuid = player.getPlayerRef().getUuid();
// Add permissions
permissions.addUserPermission(playerUuid, Set.of(
"myplugin.feature.create",
"myplugin.feature.delete"
));
// Remove permissions
permissions.removeUserPermission(playerUuid, Set.of(
"myplugin.feature.delete"
));
}
// =============================================
// Group Permissions - CORRECT
// =============================================
public void groupPermissions(Player player) {
PermissionsModule permissions = PermissionsModule.get();
UUID playerUuid = player.getPlayerRef().getUuid();
// Add player to a group
permissions.addUserToGroup(playerUuid, "moderators");
// Remove player from a group
permissions.removeUserFromGroup(playerUuid, "moderators");
// Get all groups for a player
Set<String> groups = permissions.getGroupsForUser(playerUuid);
// Add permissions to a group
permissions.addGroupPermission("moderators", Set.of(
"server.kick",
"server.mute"
));
// Remove permissions from a group
permissions.removeGroupPermission("moderators", Set.of(
"server.mute"
));
}
// =============================================
// Permission Events - CORRECT
// Note: Events use subclasses like PlayerGroupEvent.Added
// =============================================
public void registerEvents() {
// Player ADDED to group
getEventRegistry().register(PlayerGroupEvent.Added.class, event -> {
UUID playerUuid = event.getPlayerUuid();
String group = event.getGroupName(); // NOT getGroup()!
getLogger().at(Level.INFO).log("Player joined group: " + group);
});
// Player REMOVED from group
getEventRegistry().register(PlayerGroupEvent.Removed.class, event -> {
UUID playerUuid = event.getPlayerUuid();
String group = event.getGroupName();
getLogger().at(Level.INFO).log("Player left group: " + group);
});
// Group permissions ADDED
getEventRegistry().register(GroupPermissionChangeEvent.Added.class, event -> {
String group = event.getGroupName();
Set<String> added = event.getAddedPermissions();
getLogger().at(Level.INFO).log("Group " + group + " got permissions: " + added);
});
// Group permissions REMOVED
getEventRegistry().register(GroupPermissionChangeEvent.Removed.class, event -> {
String group = event.getGroupName();
Set<String> removed = event.getRemovedPermissions();
getLogger().at(Level.INFO).log("Group " + group + " lost permissions: " + removed);
});
// Player permissions ADDED
getEventRegistry().register(PlayerPermissionChangeEvent.PermissionsAdded.class, event -> {
UUID playerUuid = event.getPlayerUuid();
Set<String> added = event.getAddedPermissions();
getLogger().at(Level.INFO).log("Player got permissions: " + added);
});
// Player permissions REMOVED
getEventRegistry().register(PlayerPermissionChangeEvent.PermissionsRemoved.class, event -> {
UUID playerUuid = event.getPlayerUuid();
Set<String> removed = event.getRemovedPermissions();
getLogger().at(Level.INFO).log("Player lost permissions: " + removed);
});
}
// =============================================
// Custom Permission Provider - CORRECT interface
// Note: PermissionProvider does NOT have hasPermission(UUID, String)!
// =============================================
public static class VIPPermissionProvider implements PermissionProvider {
private final Map<UUID, Set<String>> vipPermissions = new ConcurrentHashMap<>();
private final Map<UUID, Set<String>> vipGroups = new ConcurrentHashMap<>();
@Override
@Nonnull
public String getName() {
return "VIPPermissionProvider";
}
@Override
public void addUserPermissions(@Nonnull UUID uuid, @Nonnull Set<String> permissions) {
vipPermissions.computeIfAbsent(uuid, k -> new HashSet<>()).addAll(permissions);
}
@Override
public void removeUserPermissions(@Nonnull UUID uuid, @Nonnull Set<String> permissions) {
Set<String> userPerms = vipPermissions.get(uuid);
if (userPerms != null) {
userPerms.removeAll(permissions);
}
}
@Override
public Set<String> getUserPermissions(@Nonnull UUID uuid) {
return vipPermissions.getOrDefault(uuid, Collections.emptySet());
}
@Override
public void addGroupPermissions(@Nonnull String group, @Nonnull Set<String> permissions) {
// Not implemented in this example
}
@Override
public void removeGroupPermissions(@Nonnull String group, @Nonnull Set<String> permissions) {
// Not implemented in this example
}
@Override
public Set<String> getGroupPermissions(@Nonnull String group) {
return Collections.emptySet();
}
@Override
public void addUserToGroup(@Nonnull UUID uuid, @Nonnull String group) {
vipGroups.computeIfAbsent(uuid, k -> new HashSet<>()).add(group);
}
@Override
public void removeUserFromGroup(@Nonnull UUID uuid, @Nonnull String group) {
Set<String> groups = vipGroups.get(uuid);
if (groups != null) {
groups.remove(group);
}
}
@Override
public Set<String> getGroupsForUser(@Nonnull UUID uuid) {
return vipGroups.getOrDefault(uuid, Collections.emptySet());
}
}
// =============================================
// Role-Based Access Example - CORRECT
// =============================================
public class RoleManager {
private final PermissionsModule permissions;
public RoleManager() {
this.permissions = PermissionsModule.get();
}
public void promoteToModerator(UUID playerUuid) {
permissions.addUserToGroup(playerUuid, "moderators");
permissions.addUserPermission(playerUuid, Set.of(
"server.kick",
"server.mute",
"server.warn"
));
}
public void demoteFromModerator(UUID playerUuid) {
permissions.removeUserFromGroup(playerUuid, "moderators");
permissions.removeUserPermission(playerUuid, Set.of(
"server.kick",
"server.mute",
"server.warn"
));
}
public boolean canModerate(UUID playerUuid) {
return permissions.getGroupsForUser(playerUuid).contains("moderators");
}
}
// =============================================
// Feature Gating Example - CORRECT
// =============================================
public void useFeature(Player player, String featureName) {
UUID uuid = player.getPlayerRef().getUuid();
PermissionsModule perms = PermissionsModule.get();
String permission = "myplugin.feature." + featureName;
if (!perms.hasPermission(uuid, permission)) {
player.sendMessage(Message.translation("error.no_permission"));
return;
}
// Execute feature
executeFeature(player, featureName);
}
private void executeFeature(Player player, String featureName) {
// Feature implementation
}
}

View File

@@ -0,0 +1,69 @@
package org.example.docs.reference;
/**
* Example code from: hytale-docs/content/reference/all-registries.en.md
* Tests the All Registries documentation
*/
import com.hypixel.hytale.assetstore.map.DefaultAssetMap;
import com.hypixel.hytale.event.EventPriority;
import com.hypixel.hytale.event.EventRegistry;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.event.events.player.PlayerDisconnectEvent;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
public class AllRegistriesExample {
// Placeholder - cannot extend JavaPlugin directly in test
private EventRegistry events;
private com.hypixel.hytale.server.core.command.system.CommandRegistry commands;
private java.util.logging.Logger logger;
// Example from documentation - Event Registration
public void eventRegistryExample() {
// Use getPlayerRef() - getPlayer() is deprecated
events.register(PlayerConnectEvent.class, event -> {
PlayerRef playerRef = event.getPlayerRef();
playerRef.sendMessage(Message.raw("Welcome!"));
});
events.register(EventPriority.FIRST, PlayerDisconnectEvent.class, event -> {
PlayerRef playerRef = event.getPlayerRef();
logger.log(Level.INFO, playerRef.getUsername() + " left");
});
}
// Example from documentation - Command Registration
// AbstractCommand.execute() returns CompletableFuture<Void>
public void commandRegistryExample() {
commands.registerCommand(new AbstractCommand("hello", "my_plugin.commands.hello.description") {
@Override
protected CompletableFuture<Void> execute(CommandContext ctx) {
ctx.sender().sendMessage(Message.raw("Hello, World!"));
return null; // Return null for synchronous completion
}
});
}
// CORRECTED Example from documentation - Accessing Assets
// Item.getAssetMap() returns DefaultAssetMap which takes String keys directly
// (Unlike Interaction which uses IndexedLookupTableAssetMap with int indices)
public void accessingAssetsExample() {
// Get items by string ID directly
DefaultAssetMap<String, Item> itemMap = Item.getAssetMap();
Item sword = itemMap.getAsset("iron_sword");
// Get block types - also uses string key directly
DefaultAssetMap<String, BlockType> blockMap = BlockType.getAssetMap();
BlockType stone = blockMap.getAsset("stone");
}
}

View File

@@ -0,0 +1,244 @@
package org.example.docs.tasks;
// Example from: hytale-docs/content/tasks/_index.en.md, task-registry.en.md, async-operations.en.md
// Tests the Tasks documentation examples
//
// FINDINGS:
// - TaskRegistry API is correctly documented: registerTask(CompletableFuture<Void>) and registerTask(ScheduledFuture<Void>)
// - Documentation correctly warns that runAsync(), runLater(), runRepeating() do NOT exist
// - Standard Java concurrency (CompletableFuture, ScheduledExecutorService) is the correct approach
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.task.TaskRegistration;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
/**
* Tests Tasks documentation
*/
public class TasksExample extends JavaPlugin {
private ScheduledExecutorService scheduler;
public TasksExample(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
// Register player connect event for async data loading demo
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef playerRef = event.getPlayerRef();
World world = event.getWorld();
loadPlayerData(playerRef, world);
});
}
@Override
protected void start() {
// Create scheduler for repeating tasks
scheduler = Executors.newSingleThreadScheduledExecutor();
// Schedule auto-save every 5 minutes
@SuppressWarnings("unchecked")
ScheduledFuture<Void> saveTask = (ScheduledFuture<Void>) scheduler.scheduleAtFixedRate(
() -> {
saveAllData();
getLogger().at(Level.INFO).log("Auto-save complete");
},
5, // Initial delay
5, // Period
TimeUnit.MINUTES
);
// Register for tracking
getTaskRegistry().registerTask(saveTask);
}
@Override
protected void shutdown() {
if (scheduler != null) {
scheduler.shutdown();
}
}
// =============================================
// Basic Async Pattern - CORRECT
// =============================================
public void basicAsyncExample(Player player) {
PlayerRef playerRef = player.getPlayerRef();
World world = player.getWorld();
CompletableFuture<Void> task = CompletableFuture.runAsync(() -> {
// This runs on a background thread
// Do heavy work here
String result = "Heavy computation result";
// Return to world thread for game state changes
world.execute(() -> {
playerRef.sendMessage(Message.raw("Result: " + result));
});
});
// Register with TaskRegistry for tracking
getTaskRegistry().registerTask(task);
}
// =============================================
// Delayed Task - CORRECT
// =============================================
public void delayedTaskExample(PlayerRef playerRef, World world) {
// Run after 3 seconds
CompletableFuture.delayedExecutor(3, TimeUnit.SECONDS)
.execute(() -> {
world.execute(() -> {
playerRef.sendMessage(Message.raw("3 seconds have passed!"));
});
});
}
// =============================================
// Async Data Loading - CORRECT
// =============================================
public void loadPlayerData(PlayerRef playerRef, World world) {
CompletableFuture.runAsync(() -> {
// Load from database (blocking I/O is OK here)
String data = simulateDatabaseLoad(playerRef.getUuid());
// Return to world thread for game state changes
world.execute(() -> {
Ref<EntityStore> ref = playerRef.getReference();
if (ref != null && ref.isValid()) {
playerRef.sendMessage(Message.raw("Data loaded: " + data));
}
});
});
}
// =============================================
// Countdown Timer - CORRECT
// =============================================
public void startCountdown(PlayerRef playerRef, int seconds) {
World world = Universe.get().getWorld(playerRef.getWorldUuid());
if (world == null) return;
AtomicInteger remaining = new AtomicInteger(seconds);
ScheduledExecutorService countdownScheduler = Executors.newSingleThreadScheduledExecutor();
countdownScheduler.scheduleAtFixedRate(() -> {
int count = remaining.decrementAndGet();
world.execute(() -> {
if (count > 0) {
playerRef.sendMessage(Message.raw("Starting in: " + count));
} else {
playerRef.sendMessage(Message.raw("Go!"));
countdownScheduler.shutdown();
}
});
}, 0, 1, TimeUnit.SECONDS);
}
// =============================================
// Thread-Safe Collections - CORRECT
// =============================================
private final Map<UUID, String> playerData = new ConcurrentHashMap<>();
private final java.util.Set<UUID> processing = ConcurrentHashMap.newKeySet();
public void processPlayer(PlayerRef playerRef, World world) {
UUID uuid = playerRef.getUuid();
// Prevent duplicate processing
if (!processing.add(uuid)) {
return; // Already processing
}
CompletableFuture.runAsync(() -> {
try {
String data = simulateHeavyComputation(uuid);
playerData.put(uuid, data);
world.execute(() -> {
playerRef.sendMessage(Message.raw("Processing complete!"));
});
} finally {
processing.remove(uuid);
}
});
}
// =============================================
// Error Handling - CORRECT
// =============================================
public void safeAsyncOperation(PlayerRef playerRef, World world) {
CompletableFuture.runAsync(() -> {
try {
riskyOperation(playerRef.getUuid());
world.execute(() -> {
playerRef.sendMessage(Message.raw("Operation successful!"));
});
} catch (Exception e) {
getLogger().at(Level.SEVERE).log("Async operation failed: " + e.getMessage());
world.execute(() -> {
playerRef.sendMessage(Message.raw("Operation failed. Please try again."));
});
}
});
}
// =============================================
// Chaining Async Operations - CORRECT
// =============================================
public void chainedOperations(PlayerRef playerRef, World world) {
CompletableFuture
.supplyAsync(() -> {
// First async operation
return fetchFromDatabase(playerRef.getUuid());
})
.thenApplyAsync(data -> {
// Second async operation
return processData(data);
})
.thenAccept(result -> {
// Return to world thread with final result
world.execute(() -> {
playerRef.sendMessage(Message.raw("Final result: " + result));
});
})
.exceptionally(e -> {
getLogger().at(Level.SEVERE).log("Chain failed: " + e.getMessage());
world.execute(() -> {
playerRef.sendMessage(Message.raw("Operation failed."));
});
return null;
});
}
// Helper methods
private void saveAllData() { }
private String simulateDatabaseLoad(UUID uuid) { return "data"; }
private String simulateHeavyComputation(UUID uuid) { return "result"; }
private void riskyOperation(UUID uuid) throws Exception { }
private String fetchFromDatabase(UUID uuid) { return "data"; }
private String processData(String data) { return "processed"; }
}

View File

@@ -0,0 +1,208 @@
package org.example.docs.ui;
/**
* Example code from: hytale-docs/content/ui/custom-pages.en.md
* Tests the Custom Pages documentation
*/
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime;
import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType;
import com.hypixel.hytale.protocol.packets.interface_.Page;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.entity.entities.player.pages.CustomUIPage;
import com.hypixel.hytale.server.core.entity.entities.player.pages.PageManager;
import com.hypixel.hytale.server.core.entity.entities.player.windows.ContainerWindow;
import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
import com.hypixel.hytale.server.core.ui.builder.EventData;
import com.hypixel.hytale.server.core.ui.builder.UICommandBuilder;
import com.hypixel.hytale.server.core.ui.builder.UIEventBuilder;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import java.util.List;
public class CustomPagesExample {
// Example from documentation - WelcomePage
public static class WelcomePage extends CustomUIPage {
public WelcomePage(PlayerRef playerRef) {
super(playerRef, CustomPageLifetime.CanDismiss);
}
@Override
public void build(Ref<EntityStore> ref, UICommandBuilder builder,
UIEventBuilder events, Store<EntityStore> store) {
// Load a UI template from assets
builder.append("ui/welcome_screen");
// Set dynamic values
builder.set("#player_name", playerRef.getUsername());
builder.set("#server_name", "My Server");
}
}
// Example from documentation - MenuPage with events
public static class MenuPage extends CustomUIPage {
public MenuPage(PlayerRef ref) {
super(ref, CustomPageLifetime.CanDismiss);
}
@Override
public void build(Ref<EntityStore> ref, UICommandBuilder builder,
UIEventBuilder events, Store<EntityStore> store) {
builder.append("ui/main_menu");
// Use addEventBinding with CustomUIEventBindingType and EventData
events.addEventBinding(CustomUIEventBindingType.Activating, "#play_button",
EventData.of("Action", "play"));
events.addEventBinding(CustomUIEventBindingType.Activating, "#quit_button",
EventData.of("Action", "quit"));
}
@Override
public void handleDataEvent(Ref<EntityStore> ref, Store<EntityStore> store, String rawData) {
// rawData contains JSON like: {"Action":"play"}
// Parse and handle accordingly
if (rawData.contains("\"Action\":\"play\"")) {
startGame();
} else if (rawData.contains("\"Action\":\"quit\"")) {
close();
}
}
private void startGame() {
// Game logic...
close(); // Close the page
}
}
// Example from documentation - Opening Pages
public void openingPagesExample(Player player) {
PageManager pageManager = player.getPageManager();
// Open custom page
pageManager.openCustomPage(
player.getReference(),
player.getReference().getStore(),
new MenuPage(player.getPlayerRef())
);
}
// Example from documentation - Closing Pages
public void closingPagesExample(Player player) {
PageManager pageManager = player.getPageManager();
Ref<EntityStore> ref = player.getReference();
Store<EntityStore> store = ref.getStore();
// From outside
pageManager.setPage(ref, store, Page.None);
}
// Example from documentation - Built-in pages
// NOTE: Documentation was wrong - Page.Settings doesn't exist
// Available: None, Bench, Inventory, ToolsSettings, Map, MachinimaEditor, ContentCreation, Custom
public void builtInPagesExample(Player player) {
PageManager pageManager = player.getPageManager();
Ref<EntityStore> ref = player.getReference();
Store<EntityStore> store = ref.getStore();
// Open built-in page (e.g., Inventory)
pageManager.setPage(ref, store, Page.Inventory);
}
// Example from documentation - ShopPage
public static class ShopItem {
private final String name;
private final int price;
public ShopItem(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() { return name; }
public int getPrice() { return price; }
}
public static class ShopPage extends CustomUIPage {
private int selectedItem = -1;
private final List<ShopItem> items;
public ShopPage(PlayerRef ref, List<ShopItem> items) {
super(ref, CustomPageLifetime.CanDismiss);
this.items = items;
}
@Override
public void build(Ref<EntityStore> ref, UICommandBuilder builder,
UIEventBuilder events, Store<EntityStore> store) {
builder.append("ui/shop");
// Populate items
for (int i = 0; i < items.size(); i++) {
ShopItem item = items.get(i);
builder.set("#item_" + i + "_name", item.getName());
builder.set("#item_" + i + "_price", item.getPrice());
events.addEventBinding(CustomUIEventBindingType.Activating, "#item_" + i,
EventData.of("Action", "select").append("Index", String.valueOf(i)));
}
events.addEventBinding(CustomUIEventBindingType.Activating, "#buy_button",
EventData.of("Action", "buy"));
events.addEventBinding(CustomUIEventBindingType.Activating, "#close_button",
EventData.of("Action", "close"));
}
@Override
public void handleDataEvent(Ref<EntityStore> ref, Store<EntityStore> store, String rawData) {
// rawData contains JSON like: {"Action":"select","Index":"0"}
if (rawData.contains("\"Action\":\"select\"")) {
// Extract index from JSON
int indexStart = rawData.indexOf("\"Index\":\"") + 9;
int indexEnd = rawData.indexOf("\"", indexStart);
selectedItem = Integer.parseInt(rawData.substring(indexStart, indexEnd));
updateSelection();
} else if (rawData.contains("\"Action\":\"buy\"") && selectedItem >= 0) {
purchaseItem(selectedItem);
} else if (rawData.contains("\"Action\":\"close\"")) {
close();
}
}
private void updateSelection() {
UICommandBuilder update = new UICommandBuilder();
update.set("#selected_index", selectedItem);
update.set("#selected_name", items.get(selectedItem).getName());
sendUpdate(update);
}
private void purchaseItem(int index) {
// Purchase logic...
rebuild(); // Refresh UI
}
}
// Example from documentation - Page with Windows
// NOTE: The documentation shows openCustomPageWithWindows but the signature is:
// openCustomPageWithWindows(Ref<EntityStore>, Store<EntityStore>, CustomUIPage, Window...)
public void pageWithWindowsExample(Player player, ItemContainer craftingContainer) {
PageManager pageManager = player.getPageManager();
Ref<EntityStore> ref = player.getReference();
Store<EntityStore> store = ref.getStore();
// Custom crafting UI with windows
// NOTE: ContainerWindow takes just ItemContainer, not windowId/WindowType
ContainerWindow inputWindow = new ContainerWindow(craftingContainer);
pageManager.openCustomPageWithWindows(
ref, store,
new WelcomePage(player.getPlayerRef()),
inputWindow
);
}
}

View File

@@ -0,0 +1,149 @@
package org.example.docs.ui;
/**
* Example code from: hytale-docs/content/ui/hud-manager.en.md
* Tests the HUD Manager documentation
*/
import com.hypixel.hytale.protocol.packets.interface_.HudComponent;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.entity.entities.player.hud.CustomUIHud;
import com.hypixel.hytale.server.core.entity.entities.player.hud.HudManager;
import com.hypixel.hytale.server.core.ui.builder.UICommandBuilder;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import java.util.HashSet;
import java.util.Set;
public class HudManagerExample {
// Example from documentation - Accessing HUD Manager
public void accessingHudManager(Player player) {
HudManager hudManager = player.getHudManager();
}
// Example from documentation - Showing Components
public void showingComponents(Player player) {
HudManager hudManager = player.getHudManager();
// Show specific components (additive)
hudManager.showHudComponents(player.getPlayerRef(),
HudComponent.Health,
HudComponent.Stamina,
HudComponent.Hotbar
);
// Show from a Set
Set<HudComponent> components = Set.of(
HudComponent.Chat,
HudComponent.Notifications
);
hudManager.showHudComponents(player.getPlayerRef(), components);
}
// Example from documentation - Hiding Components
public void hidingComponents(Player player) {
HudManager hudManager = player.getHudManager();
// Hide specific components
hudManager.hideHudComponents(player.getPlayerRef(),
HudComponent.Compass,
HudComponent.Speedometer
);
}
// Example from documentation - Setting Components
public void settingComponents(Player player) {
HudManager hudManager = player.getHudManager();
// Set exact visible components (replaces all)
hudManager.setVisibleHudComponents(player.getPlayerRef(),
HudComponent.Hotbar,
HudComponent.Health
);
// Clear all HUD components (empty)
hudManager.setVisibleHudComponents(player.getPlayerRef());
}
// Example from documentation - Querying Visible Components
public void queryingComponents(Player player) {
HudManager hudManager = player.getHudManager();
Set<HudComponent> visible = hudManager.getVisibleHudComponents();
if (visible.contains(HudComponent.Health)) {
// Health bar is visible
}
}
// Example from documentation - Resetting HUD
public void resettingHud(Player player) {
HudManager hudManager = player.getHudManager();
// Reset to default components and clear custom HUD
hudManager.resetHud(player.getPlayerRef());
// Reset entire UI state
hudManager.resetUserInterface(player.getPlayerRef());
}
// Example from documentation - Custom HUD Overlay
public static class ScoreboardHud extends CustomUIHud {
public ScoreboardHud(PlayerRef ref) {
super(ref);
}
@Override
protected void build(UICommandBuilder builder) {
builder.append("ui/scoreboard");
builder.set("#score", 1500);
builder.set("#rank", "Gold");
}
}
public void customHudOverlay(Player player) {
HudManager hudManager = player.getHudManager();
// Set custom HUD
hudManager.setCustomHud(player.getPlayerRef(), new ScoreboardHud(player.getPlayerRef()));
// Remove custom HUD
hudManager.setCustomHud(player.getPlayerRef(), null);
}
// Example from documentation - Cinematic Mode
public void enterCinematicMode(Player player) {
HudManager hud = player.getHudManager();
// Store current state if needed for restoration
Set<HudComponent> previous = new HashSet<>(hud.getVisibleHudComponents());
// Clear all HUD
hud.setVisibleHudComponents(player.getPlayerRef());
}
public void exitCinematicMode(Player player, Set<HudComponent> restore) {
player.getHudManager().setVisibleHudComponents(player.getPlayerRef(), restore);
}
// Example from documentation - Minimal HUD Mode
public void setMinimalHud(Player player) {
player.getHudManager().setVisibleHudComponents(player.getPlayerRef(),
HudComponent.Hotbar,
HudComponent.Health,
HudComponent.Reticle
);
}
// Example from documentation - Builder Mode HUD
public void enableBuilderHud(Player player) {
HudManager hud = player.getHudManager();
hud.showHudComponents(player.getPlayerRef(),
HudComponent.BuilderToolsLegend,
HudComponent.BuilderToolsMaterialSlotSelector,
HudComponent.BlockVariantSelector
);
}
}

View File

@@ -0,0 +1,66 @@
package org.example.docs.ui;
/**
* Example code from: hytale-docs/content/ui/_index.en.md
* Tests the UI system overview documentation
*/
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime;
import com.hypixel.hytale.protocol.packets.interface_.HudComponent;
import com.hypixel.hytale.protocol.packets.interface_.Page;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.entity.entities.player.hud.HudManager;
import com.hypixel.hytale.server.core.entity.entities.player.pages.CustomUIPage;
import com.hypixel.hytale.server.core.entity.entities.player.pages.PageManager;
import com.hypixel.hytale.server.core.ui.builder.UICommandBuilder;
import com.hypixel.hytale.server.core.ui.builder.UIEventBuilder;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
public class UiIndexExample {
// Example from documentation - Custom Page
public static class MyPage extends CustomUIPage {
public MyPage(PlayerRef ref) {
super(ref, CustomPageLifetime.CanDismiss);
}
@Override
public void build(Ref<EntityStore> ref, UICommandBuilder builder,
UIEventBuilder events, Store<EntityStore> store) {
// Build UI using builder.set(), builder.append(), etc.
builder.append("ui/my_template");
builder.set("#title", "Welcome!");
}
}
// Example from documentation - Page Manager
public void pageManagerExample(Player player) {
Ref<EntityStore> ref = player.getReference();
Store<EntityStore> store = ref.getStore();
PageManager pageManager = player.getPageManager();
// Open a custom page
pageManager.openCustomPage(ref, store, new MyPage(player.getPlayerRef()));
// Close to normal view
pageManager.setPage(ref, store, Page.None);
}
// Example from documentation - HUD Manager
public void hudManagerExample(Player player) {
HudManager hudManager = player.getHudManager();
// Hide all HUD components
hudManager.setVisibleHudComponents(player.getPlayerRef());
// Show specific components
hudManager.showHudComponents(player.getPlayerRef(),
HudComponent.Hotbar,
HudComponent.Health
);
}
}

View File

@@ -0,0 +1,83 @@
package org.example.docs.ui;
/**
* Example code from: hytale-docs/content/ui/windows.en.md
* Tests the Windows documentation
*
* NOTE: The documentation has some errors:
* - ContainerWindow constructor is ContainerWindow(ItemContainer), not ContainerWindow(id, WindowType, container)
* - BlockWindow is abstract and cannot be instantiated directly
* - ItemStackContainerWindow constructor is ItemStackContainerWindow(ItemStackItemContainer)
*/
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.protocol.packets.interface_.Page;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.entity.entities.player.pages.PageManager;
import com.hypixel.hytale.server.core.entity.entities.player.windows.ContainerWindow;
import com.hypixel.hytale.server.core.entity.entities.player.windows.ItemStackContainerWindow;
import com.hypixel.hytale.server.core.entity.entities.player.windows.WindowManager;
import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
import com.hypixel.hytale.server.core.inventory.container.ItemStackItemContainer;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
public class WindowsExample {
// Example from documentation - Window Manager
public void windowManagerExample(Player player) {
WindowManager windowManager = player.getWindowManager();
}
// Example from documentation - ContainerWindow
// CORRECTED: Constructor is ContainerWindow(ItemContainer)
public void containerWindowExample(ItemContainer container) {
// Open a container window
ContainerWindow window = new ContainerWindow(container);
}
// Example from documentation - ItemStackContainerWindow
// CORRECTED: Constructor is ItemStackContainerWindow(ItemStackItemContainer)
public void itemStackContainerWindowExample(ItemStackItemContainer itemStackContainer) {
ItemStackContainerWindow window = new ItemStackContainerWindow(itemStackContainer);
}
// Example from documentation - Opening Windows
// CORRECTED: ContainerWindow takes just ItemContainer
public void openingWindowsExample(Player player, ItemContainer chestContainer) {
PageManager pageManager = player.getPageManager();
Ref<EntityStore> ref = player.getReference();
Store<EntityStore> store = ref.getStore();
// Open page with inventory windows
ContainerWindow chestWindow = new ContainerWindow(chestContainer);
boolean success = pageManager.setPageWithWindows(
ref, store,
Page.None, // or Page.Custom
true, // canCloseThroughInteraction
chestWindow
);
}
// Example from documentation - Chest Container UI
// CORRECTED: ContainerWindow takes just ItemContainer
public void openChest(Player player, ItemContainer chestContainer) {
PageManager pageManager = player.getPageManager();
Ref<EntityStore> ref = player.getReference();
Store<EntityStore> store = ref.getStore();
ContainerWindow window = new ContainerWindow(chestContainer);
pageManager.setPageWithWindows(
ref, store,
Page.None,
true, // Allow closing via interaction
window
);
}
// NOTE: BlockWindow is abstract - cannot be instantiated directly
// The documentation example is incorrect
// You would need to use a concrete subclass or the builtin interactions
}

View File

@@ -0,0 +1,197 @@
package org.example.docs.world;
// Example from: hytale-docs/content/world/blocks.en.md
// Tests the blocks documentation examples
//
// FINDINGS:
// - Vector3i uses getX(), getY(), getZ() - NOT x(), y(), z()
// - Rotation values are None, Ninety, OneEighty, TwoSeventy - NOT North, East, etc.
// - World does NOT implement BlockAccessor directly - placeBlock is on WorldChunk
// - For placeBlock with rotation, you MUST get the chunk first:
// WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(x, z));
// chunk.placeBlock(x, y, z, "block_key", rotation...);
// - World-level methods available: setBlock, getBlock, breakBlock, getBlockType
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;
import com.hypixel.hytale.server.core.event.events.ecs.BreakBlockEvent;
import com.hypixel.hytale.server.core.event.events.ecs.PlaceBlockEvent;
import com.hypixel.hytale.server.core.event.events.ecs.DamageBlockEvent;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.protocol.BlockMaterial;
import java.util.logging.Level;
/**
* Tests blocks.en.md documentation
*/
public class BlocksExample extends JavaPlugin {
public BlocksExample(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
// =============================================
// Block Events
// =============================================
// BreakBlockEvent
getEventRegistry().register(BreakBlockEvent.class, event -> {
Vector3i position = event.getTargetBlock();
BlockType blockType = event.getBlockType();
getLogger().at(Level.INFO).log("Block broken at " + position);
// Cancel if needed
// event.setCancelled(true);
});
// PlaceBlockEvent
getEventRegistry().register(PlaceBlockEvent.class, event -> {
Vector3i position = event.getTargetBlock();
RotationTuple rotation = event.getRotation();
getLogger().at(Level.INFO).log("Block placed at " + position);
// Cancel if needed
// event.setCancelled(true);
});
// DamageBlockEvent
getEventRegistry().register(DamageBlockEvent.class, event -> {
Vector3i position = event.getTargetBlock();
BlockType blockType = event.getBlockType();
// Cancel to prevent damage
// event.setCancelled(true);
});
}
// =============================================
// Block manipulation examples
// =============================================
public void blockManipulationExamples(World world) {
int x = 100, y = 64, z = 200;
// Get block ID at position
int blockId = world.getBlock(x, y, z);
int blockIdVec = world.getBlock(new Vector3i(x, y, z));
// Get BlockType at position
BlockType blockType = world.getBlockType(x, y, z);
BlockType blockTypeVec = world.getBlockType(new Vector3i(x, y, z));
// Check if block is empty
if (blockType == BlockType.EMPTY) {
// Air or empty space
}
// Set block by block type key
world.setBlock(x, y, z, "stone");
world.setBlock(x, y, z, "oak_planks", 0);
// Set block by BlockType object - use getId() to get the key string
BlockType stone = BlockType.getAssetMap().getAsset("stone");
if (stone != null) {
world.setBlock(x, y, z, stone.getId());
}
// Get block ID index (useful for internal operations)
int stoneId = BlockType.getAssetMap().getIndex("stone");
// NOTE: World.setBlock takes a String blockTypeKey, NOT a block ID int
// Use the string key directly:
world.setBlock(x, y, z, "stone");
// Place block with rotation
// NOTE: placeBlock is on WorldChunk (BlockAccessor), NOT on World directly!
// Must get the chunk first
WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(x, z));
if (chunk != null) {
// Rotation values are None, Ninety, OneEighty, TwoSeventy
chunk.placeBlock(x, y, z, "oak_log",
Rotation.Ninety, // yaw (90 degrees)
Rotation.None, // pitch
Rotation.None // roll
);
// Place with rotation tuple
chunk.placeBlock(x, y, z, "oak_log",
RotationTuple.of(Rotation.Ninety, Rotation.None, Rotation.None),
0, // settings
true // validatePlacement
);
}
// Break block (requires settings parameter)
world.breakBlock(x, y, z, 0); // 0 = default settings
// Block interaction state
BlockType currentBlock = world.getBlockType(x, y, z);
if (currentBlock != null) {
world.setBlockInteractionState(
new Vector3i(x, y, z),
currentBlock,
"open"
);
}
}
// =============================================
// BlockType asset access
// =============================================
public void blockTypeExamples() {
// Get block type by ID
BlockType blockType = BlockType.getAssetMap().getAsset("stone");
if (blockType != null) {
// Get block ID
String blockId = blockType.getId();
// Get material
BlockMaterial material = blockType.getMaterial();
}
// Get block ID index
int blockIndex = BlockType.getAssetMap().getIndex("stone");
// Get block from index
BlockType fromIndex = BlockType.getAssetMap().getAsset(blockIndex);
}
// =============================================
// Utility methods
// NOTE: Uses getX(), getY(), getZ() - NOT x(), y(), z()
// =============================================
public boolean containsBlockType(World world, Vector3i min, Vector3i max, String blockTypeKey) {
for (int x = min.getX(); x <= max.getX(); x++) {
for (int y = min.getY(); y <= max.getY(); y++) {
for (int z = min.getZ(); z <= max.getZ(); z++) {
BlockType block = world.getBlockType(x, y, z);
if (block != null && block.getId().equals(blockTypeKey)) {
return true;
}
}
}
}
return false;
}
public void fillArea(World world, Vector3i min, Vector3i max, String blockTypeKey) {
for (int x = min.getX(); x <= max.getX(); x++) {
for (int y = min.getY(); y <= max.getY(); y++) {
for (int z = min.getZ(); z <= max.getZ(); z++) {
world.setBlock(x, y, z, blockTypeKey);
}
}
}
}
}

View File

@@ -0,0 +1,156 @@
package org.example.docs.world;
// Example from: hytale-docs/content/world/chunks.en.md
// Tests the chunks documentation examples
//
// FINDINGS:
// - Vector3d uses public fields x, y, z OR getX(), getY(), getZ() - NOT x(), y(), z()
// - Vector3i uses getX(), getY(), getZ() - NOT x(), y(), z()
// - Player.getPosition() does NOT exist - use PlayerRef.getTransform().getPosition()
// - Vector2i uses getX(), getY() - NOT x(), y()
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.math.vector.Vector2i;
import com.hypixel.hytale.math.vector.Transform;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
/**
* Tests chunks.en.md documentation
*
* IMPORTANT:
* - Player.getPosition() DOESN'T EXIST - use PlayerRef.getTransform().getPosition()
* - Vector3d uses x, y, z fields or getX(), getY(), getZ() - NOT x(), y(), z()
* - Vector3i uses getX(), getY(), getZ() - NOT x(), y(), z()
*/
public class ChunksExample extends JavaPlugin {
public ChunksExample(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
// =============================================
// Chunk Basics
// =============================================
World world = Universe.get().getWorld("default");
int x = 100, y = 64, z = 200;
// Access blocks within a chunk through the World API
BlockType blockType = world.getBlockType(x, y, z);
// =============================================
// Chunk Coordinates
// =============================================
int blockX = 100, blockZ = 200;
// Convert block coordinates to chunk coordinates
int chunkX = blockX >> 4; // Divide by 16
int chunkZ = blockZ >> 4;
// Convert chunk coordinates to block coordinates (corner)
int startBlockX = chunkX << 4; // Multiply by 16
int startBlockZ = chunkZ << 4;
// =============================================
// Chunk Loading - CORRECTED
// =============================================
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef playerRef = event.getPlayerRef();
// CORRECT: Get position from PlayerRef.getTransform()
// Player.getPosition() does NOT exist!
Transform transform = playerRef.getTransform();
if (transform != null) {
Vector3d pos = transform.getPosition();
// Vector3d uses getX(), getY(), getZ() - NOT x(), y(), z()
int pChunkX = (int) pos.getX() >> 4;
int pChunkZ = (int) pos.getZ() >> 4;
getLogger().at(Level.INFO).log("Player at chunk: " + pChunkX + ", " + pChunkZ);
}
});
}
// =============================================
// Working with Chunks
// =============================================
public void iterateChunkBlocks(World world, int chunkX, int chunkZ) {
int startX = chunkX << 4;
int startZ = chunkZ << 4;
for (int x = startX; x < startX + 16; x++) {
for (int z = startZ; z < startZ + 16; z++) {
for (int y = 0; y < 256; y++) {
BlockType blockType = world.getBlockType(x, y, z);
// Process block...
}
}
}
}
public void processChunk(World world, int chunkX, int chunkZ) {
int baseX = chunkX << 4;
int baseZ = chunkZ << 4;
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
int worldX = baseX + x;
int worldZ = baseZ + z;
// Process...
}
}
}
// =============================================
// Async chunk processing - CORRECTED
// =============================================
public void processChunksAsync(PlayerRef playerRef, World world) {
CompletableFuture.runAsync(() -> {
// Heavy chunk processing here
Map<Vector3i, String> results = new HashMap<>();
// ... process
// Return to world thread to apply results
world.execute(() -> {
for (var entry : results.entrySet()) {
Vector3i pos = entry.getKey();
// CORRECT: Vector3i uses getX(), getY(), getZ()
world.setBlock(pos.getX(), pos.getY(), pos.getZ(), entry.getValue());
}
playerRef.sendMessage(Message.raw("Chunk processing complete!"));
});
});
}
// =============================================
// Get chunk coords from player - CORRECTED
// =============================================
public Vector2i getChunkCoords(PlayerRef playerRef) {
// CORRECT: Use PlayerRef.getTransform() - Player.getPosition() doesn't exist!
Transform transform = playerRef.getTransform();
if (transform == null) {
return new Vector2i(0, 0);
}
Vector3d pos = transform.getPosition();
// CORRECT: Vector3d uses getX(), getZ() - NOT x(), z()
int chunkX = (int) pos.getX() >> 4;
int chunkZ = (int) pos.getZ() >> 4;
return new Vector2i(chunkX, chunkZ);
}
}

View File

@@ -0,0 +1,117 @@
package org.example.docs.world;
// Example from: hytale-docs/content/world/player-refs.en.md
// Tests the PlayerRef documentation examples
//
// FINDINGS:
// - PlayerRef.getName() does NOT exist - use getUsername()
// - PlayerRef.getPlayer() does NOT exist
// - PlayerRef.sendMessage(Message) exists directly, not via getPlayer()
// - Player.getPlayerRef() EXISTS
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.event.events.player.PlayerDisconnectEvent;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.component.Ref;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
/**
* Tests player-refs.en.md documentation
*/
public class PlayerRefsExample extends JavaPlugin {
private final Map<UUID, PlayerData> playerData = new ConcurrentHashMap<>();
public PlayerRefsExample(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
// =============================================
// Getting a PlayerRef - CORRECT
// =============================================
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef ref = event.getPlayerRef();
// PlayerRef methods that EXIST:
UUID uuid = ref.getUuid();
String name = ref.getUsername(); // NOT getName()!
// sendMessage is DIRECT on PlayerRef
ref.sendMessage(Message.raw("Welcome!"));
// Get entity reference
Ref<EntityStore> entityRef = ref.getReference();
// Check validity
boolean valid = ref.isValid();
// Store player data
playerData.put(ref.getUuid(), new PlayerData(ref));
});
// Disconnect handling
getEventRegistry().register(PlayerDisconnectEvent.class, event -> {
playerData.remove(event.getPlayerRef().getUuid());
});
}
// =============================================
// World PlayerRefs - CORRECT
// =============================================
public void worldPlayerRefs() {
World world = Universe.get().getWorld("default");
if (world != null) {
// Get all player refs in this world
Collection<PlayerRef> playerRefs = world.getPlayerRefs();
// Get all active players (returns List, not Collection)
List<Player> players = world.getPlayers();
}
}
// =============================================
// Cross-World Messaging - CORRECT
// =============================================
public void sendGlobalMessage(PlayerRef ref, String message) {
// PlayerRef.sendMessage handles cross-world messaging directly
ref.sendMessage(Message.raw(message));
}
public void broadcastAll(String message) {
Message msg = Message.raw(message);
for (World world : Universe.get().getWorlds().values()) {
for (PlayerRef ref : world.getPlayerRefs()) {
ref.sendMessage(msg);
}
}
}
// Simple player data class
static class PlayerData {
private final PlayerRef playerRef;
private int score;
public PlayerData(PlayerRef ref) {
this.playerRef = ref;
}
public void addScore(int amount) {
this.score += amount;
playerRef.sendMessage(Message.raw("Score: " + score));
}
}
}

View File

@@ -0,0 +1,106 @@
package org.example.docs.world;
// Example from: hytale-docs/content/world/universe-and-worlds.en.md
// Tests the universe and worlds documentation examples
//
// FINDINGS:
// - universe.hasWorld() does NOT exist - use universe.getWorld(name) != null
// - world.getEntities() does NOT exist on World
// - world.setBlock(x, y, z, blockType) wrong - takes String, not BlockType
// - world.getSpawnPoint() does NOT exist - access via WorldConfig.getSpawnProvider()
// - world.getPlayers() returns List<Player>, not Collection<Player>
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.math.vector.Vector3i;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
/**
* Tests universe-and-worlds.en.md documentation
*/
public class UniverseWorldsExample extends JavaPlugin {
public UniverseWorldsExample(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
// =============================================
// Universe - CORRECT patterns
// =============================================
Universe universe = Universe.get();
// Get specific world
World world = universe.getWorld("default");
// Get all worlds
Map<String, World> worlds = universe.getWorlds();
// Check if world exists - CORRECT way (hasWorld doesn't exist!)
if (universe.getWorld("creative") != null) {
// World exists
}
// =============================================
// World - CORRECT patterns
// =============================================
if (world != null) {
// Get world name
String name = world.getName();
// Get all players - returns List<Player>, not Collection<Player>
List<Player> players = world.getPlayers();
// Get player refs
Collection<PlayerRef> playerRefs = world.getPlayerRefs();
// NOTE: world.getEntities() does NOT exist on World!
// Entities are accessed through EntityStore
}
// =============================================
// World Operations - CORRECT patterns
// =============================================
if (world != null) {
int x = 100, y = 64, z = 200;
// Get block at position
BlockType blockType = world.getBlockType(x, y, z);
BlockType blockTypeVec = world.getBlockType(new Vector3i(x, y, z));
// Set block - takes String, NOT BlockType!
world.setBlock(x, y, z, "stone");
// If you have a BlockType, use getId()
if (blockType != null) {
world.setBlock(x, y, z, blockType.getId());
}
// NOTE: world.getSpawnPoint() does NOT exist directly!
// Access spawn via: world.getWorldConfig().getSpawnProvider()
}
// =============================================
// World from Event - CORRECT
// =============================================
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef playerRef = event.getPlayerRef();
World eventWorld = event.getWorld(); // Can be null!
if (eventWorld != null) {
getLogger().at(Level.INFO).log("Player joined world: " + eventWorld.getName());
}
});
}
}

View File

@@ -0,0 +1,18 @@
{
"Group": "org.example",
"Name": "ExamplePlugin",
"Version": "1.0.0",
"Description": "An example Hytale server plugin",
"Authors": [
{
"Name": "Your Name"
}
],
"Main": "org.example.ExamplePlugin",
"ServerVersion": "*",
"Dependencies": {},
"OptionalDependencies": {},
"DisabledByDefault": false,
"IncludesAssetPack": false,
"SubPlugins": []
}

View File

@@ -0,0 +1,12 @@
package org.example;
import org.junit.Test;
import static org.junit.Assert.*;
public class ExamplePluginTest {
@Test
public void pluginJsonExists() {
// Verify manifest.json exists in resources
assertNotNull(getClass().getClassLoader().getResource("manifest.json"));
}
}

6
build.gradle.kts Normal file
View File

@@ -0,0 +1,6 @@
plugins {
java
}
// Root project configuration
// The actual plugin code is in the 'app' module

5
gradle.properties Normal file
View File

@@ -0,0 +1,5 @@
# This file was generated by the Gradle 'init' task.
# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties
org.gradle.configuration-cache=true

10
gradle/libs.versions.toml Normal file
View File

@@ -0,0 +1,10 @@
# This file was generated by the Gradle 'init' task.
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format
[versions]
guava = "33.4.6-jre"
junit = "4.13.2"
[libraries]
guava = { module = "com.google.guava:guava", version.ref = "guava" }
junit = { module = "junit:junit", version.ref = "junit" }

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

248
gradlew vendored Normal file
View 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
gradlew.bat vendored Normal file
View 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

7
settings.gradle.kts Normal file
View File

@@ -0,0 +1,7 @@
plugins {
// Apply the foojay-resolver plugin to allow automatic download of JDKs
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}
rootProject.name = "example-plugin"
include("app")

140
setup.sh Normal file
View File

@@ -0,0 +1,140 @@
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
BIN_DIR="$SCRIPT_DIR/.bin"
SERVER_DIR="$SCRIPT_DIR/server"
SRC_REF_DIR="$SCRIPT_DIR/src-ref"
# Détection de l'OS
detect_os() {
case "$(uname -s)" in
Linux*) echo "linux-amd64" ;;
Darwin*) echo "darwin-amd64" ;;
CYGWIN*|MINGW*|MSYS*) echo "windows-amd64.exe" ;;
*) echo "unknown" ;;
esac
}
OS_SUFFIX=$(detect_os)
if [ "$OS_SUFFIX" = "unknown" ]; then
echo "Erreur: OS non supporté"
exit 1
fi
download() {
mkdir -p "$BIN_DIR"
echo "Téléchargement de hytale-downloader..."
curl -L -o "$BIN_DIR/hytale-downloader.zip" "https://downloader.hytale.com/hytale-downloader.zip"
unzip -o "$BIN_DIR/hytale-downloader.zip" -d "$BIN_DIR"
rm "$BIN_DIR/hytale-downloader.zip"
echo "Téléchargement de CFR..."
curl -L -o "$BIN_DIR/cfr-0.152.jar" "https://www.benf.org/other/cfr/cfr-0.152.jar"
# Rendre les binaires exécutables
chmod +x "$BIN_DIR"/hytale-downloader-* 2>/dev/null || true
echo "Téléchargement terminé."
}
setup() {
DOWNLOADER="$BIN_DIR/hytale-downloader-$OS_SUFFIX"
if [ ! -f "$DOWNLOADER" ]; then
echo "Erreur: $DOWNLOADER non trouvé."
echo "Exécutez d'abord: ./setup.sh --download"
exit 1
fi
echo "Téléchargement du serveur Hytale..."
if [ ! -d "$SERVER_DIR" ]; then
mkdir $SERVER_DIR
fi
if [ ! -f "$SERVER_DIR/server.zip" ]; then
"$DOWNLOADER" --download-path "$SERVER_DIR/server.zip"
fi
echo "Décompression de l'archive du serveur Hytale"
unzip -o "$SERVER_DIR/server.zip" -d "$SERVER_DIR"
echo "Téléchargement terminée."
}
update_safe() {
echo "Are you sure ?"
echo "It will delete the old server/Assets.zip server/server.zip server/Server/* "
echo "Use ./setup.sh --update-sure"
}
update() {
DOWNLOADER="$BIN_DIR/hytale-downloader-$OS_SUFFIX"
echo "Téléchargement du serveur Hytale..."
if [ ! -d "$SERVER_DIR" ]; then
mkdir $SERVER_DIR
fi
if [ -f "$SERVER_DIR/server.zip" ]; then
rm $SERVER_DIR/server.zip
fi
if [ -f "$SERVER_DIR/Assets.zip" ]; then
rm $SERVER_DIR/Assets.zip
fi
if [ -d "$SERVER_DIR/Server.zip" ]; then
rm -rf $SERVER_DIR/Server
fi
if [ ! -f "$SERVER_DIR/server.zip" ]; then
"$DOWNLOADER" --download-path "$SERVER_DIR/server.zip"
fi
echo "Décompression de l'archive du serveur Hytale"
unzip -o "$SERVER_DIR/server.zip" -d "$SERVER_DIR"
echo "Téléchargement terminée."
}
update-pre() {
DOWNLOADER="$BIN_DIR/hytale-downloader-$OS_SUFFIX"
echo "Téléchargement du serveur Hytale..."
if [ ! -d "$SERVER_DIR" ]; then
mkdir $SERVER_DIR
fi
if [ -f "$SERVER_DIR/server.zip" ]; then
rm $SERVER_DIR/server.zip
fi
if [ -f "$SERVER_DIR/Assets.zip" ]; then
rm $SERVER_DIR/Assets.zip
fi
if [ -d "$SERVER_DIR/Server.zip" ]; then
rm -rf $SERVER_DIR/Server
fi
if [ ! -f "$SERVER_DIR/server.zip" ]; then
"$DOWNLOADER" --download-path "$SERVER_DIR/server.zip" --patchline pre-release
fi
echo "Décompression de l'archive du serveur Hytale"
unzip -o "$SERVER_DIR/server.zip" -d "$SERVER_DIR"
echo "Téléchargement terminée."
}
decompile() {
echo "Décompilation avec CFR..."
rm -rf "$SRC_REF_DIR/*"
java -jar "$BIN_DIR/cfr-0.152.jar" "$SERVER_DIR/Server/HytaleServer.jar" --outputdir "$SRC_REF_DIR"
echo "Décompilation terminée."
}
case "$1" in
--download) download ;;
--setup) setup ;;
--decompile)decompile ;;
--update) update ;;
--update-pre) update-pre ;;
*)
echo "Usage: ./setup.sh [--download|--setup]"
echo ""
echo "Options:"
echo " --download Télécharge les outils (hytale-downloader, CFR)"
echo " --setup Configure l'environnement (télécharge le serveur)"
echo " --update Télécharge la mise à jour"
echo " --update-pre Télécharge la mise à jour pre-release"
echo " --decompile Décompile le serveur"
exit 1
;;
esac