Initial commit
This commit is contained in:
27
.gitignore
vendored
Normal file
27
.gitignore
vendored
Normal 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
99
README.md
Normal 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
29
app/build.gradle.kts
Normal 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")
|
||||
}
|
||||
33
app/src/main/java/org/example/ExamplePlugin.java
Normal file
33
app/src/main/java/org/example/ExamplePlugin.java
Normal 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() {
|
||||
}
|
||||
}
|
||||
31
app/src/main/java/org/example/MyConfig.java
Normal file
31
app/src/main/java/org/example/MyConfig.java
Normal 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; }
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
144
app/src/main/java/org/example/docs/effects/ParticlesExample.java
Normal file
144
app/src/main/java/org/example/docs/effects/ParticlesExample.java
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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!
|
||||
}
|
||||
}
|
||||
@@ -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!
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
244
app/src/main/java/org/example/docs/tasks/TasksExample.java
Normal file
244
app/src/main/java/org/example/docs/tasks/TasksExample.java
Normal 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"; }
|
||||
}
|
||||
208
app/src/main/java/org/example/docs/ui/CustomPagesExample.java
Normal file
208
app/src/main/java/org/example/docs/ui/CustomPagesExample.java
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
||||
149
app/src/main/java/org/example/docs/ui/HudManagerExample.java
Normal file
149
app/src/main/java/org/example/docs/ui/HudManagerExample.java
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
||||
66
app/src/main/java/org/example/docs/ui/UiIndexExample.java
Normal file
66
app/src/main/java/org/example/docs/ui/UiIndexExample.java
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
||||
83
app/src/main/java/org/example/docs/ui/WindowsExample.java
Normal file
83
app/src/main/java/org/example/docs/ui/WindowsExample.java
Normal 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
|
||||
}
|
||||
197
app/src/main/java/org/example/docs/world/BlocksExample.java
Normal file
197
app/src/main/java/org/example/docs/world/BlocksExample.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
156
app/src/main/java/org/example/docs/world/ChunksExample.java
Normal file
156
app/src/main/java/org/example/docs/world/ChunksExample.java
Normal 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);
|
||||
}
|
||||
}
|
||||
117
app/src/main/java/org/example/docs/world/PlayerRefsExample.java
Normal file
117
app/src/main/java/org/example/docs/world/PlayerRefsExample.java
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
18
app/src/main/resources/manifest.json
Normal file
18
app/src/main/resources/manifest.json
Normal 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": []
|
||||
}
|
||||
12
app/src/test/java/org/example/ExamplePluginTest.java
Normal file
12
app/src/test/java/org/example/ExamplePluginTest.java
Normal 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
6
build.gradle.kts
Normal file
@@ -0,0 +1,6 @@
|
||||
plugins {
|
||||
java
|
||||
}
|
||||
|
||||
// Root project configuration
|
||||
// The actual plugin code is in the 'app' module
|
||||
5
gradle.properties
Normal file
5
gradle.properties
Normal 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
10
gradle/libs.versions.toml
Normal 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
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal 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
248
gradlew
vendored
Normal 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
93
gradlew.bat
vendored
Normal 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
7
settings.gradle.kts
Normal 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
140
setup.sh
Normal 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
|
||||
Reference in New Issue
Block a user