Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
fdada52
refactor: use ast for template processing (wip)
Farrael Feb 2, 2026
73299fb
Merge remote-tracking branch 'upstream/main' into feature/template_pr…
Farrael Feb 2, 2026
8a611c2
doc: add license header / refactor: reflection class update
Farrael Feb 2, 2026
8cecafa
refactor: handle size and symbol from type
Farrael Feb 3, 2026
450026e
feat: handle components and childs with cache
Farrael Feb 5, 2026
fd7a816
Merge remote-tracking branch 'upstream/main' into feature/template_ast
Farrael Feb 5, 2026
f33fd3a
feat: Add variable function to replace method call on objects
Farrael Feb 6, 2026
a45cacb
feat: process dynamique variables as attributes
Farrael Feb 6, 2026
a0df2bc
Merge remote-tracking branch 'upstream/main' into feature/template_ast
Farrael Feb 6, 2026
2e3358e
fix: missing imports
Farrael Feb 6, 2026
744b12c
fix: address review feedback
Farrael Feb 9, 2026
7d4fc9e
feat: add caching policy for variables in template
Farrael Feb 9, 2026
e208876
feat: right variable not always evaluated `and` / new policies for va…
Farrael Feb 9, 2026
c07a8ad
feat: replace custom component children with slots
Farrael Feb 11, 2026
b3f5b50
refactor: removed all parsing logic from lexer
Farrael Feb 12, 2026
95b3686
feat: control flow as attributes
Farrael Feb 13, 2026
b986f39
refactor: replace string manipulation with custom reader
Farrael Feb 13, 2026
b90b018
Merge remote-tracking branch 'upstream/main' into feature/template_ast
Farrael Feb 13, 2026
c6f129d
Enhance control flow attributes with else-if and else support
KelpyCode Feb 13, 2026
4dd5a07
Add thread-safe page update methods
KelpyCode Feb 13, 2026
dbdc0b2
Implement inline if block parsing and support for single-quoted strin…
KelpyCode Feb 13, 2026
11bef21
Enhance HTML processing by adding support for void elements and impro…
KelpyCode Feb 13, 2026
3835503
Removed unused InputTagTest file
KelpyCode Feb 14, 2026
2b14201
refactor: rework the conditional structure / cleanup
Farrael Feb 15, 2026
2eda13b
refactor: update parser unknown keywork handling
Farrael Feb 15, 2026
f2e904c
Merge remote-tracking branch 'upstream/main' into feature/template_ast
Farrael Feb 15, 2026
2a1244e
feat: handle html comments
Farrael Feb 15, 2026
eab83f5
fix: optional value should resolve text node
Farrael Feb 15, 2026
2b65cf9
feat: optional value as variable
Farrael Feb 15, 2026
02b3596
fix: missing optional get from template variable
Farrael Feb 16, 2026
e5a7336
fix: attributes for / if does not eat enough space
Farrael Feb 16, 2026
bb76d03
feat: add negative variables / event registration on template
Farrael Feb 17, 2026
badb550
rework: add event type to callback arguments
Farrael Feb 17, 2026
59cc4f0
refactor: update dev commands
Farrael Feb 18, 2026
93f4eac
Merge remote-tracking branch 'upstream/main' into feature/template_ast
Farrael Feb 19, 2026
60dad46
fix: ignore template for conditional components
Farrael Feb 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ tasks.test {
jvmArgs "-Djava.util.logging.manager=com.hypixel.hytale.logger.backend.HytaleLogManager"

testLogging {
showStandardStreams = true
events "passed", "failed", "skipped"
}
}
79 changes: 34 additions & 45 deletions src/main/java/au/ellie/hyui/HyUIPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,10 @@
import au.ellie.hyui.utils.HyvatarUtils;
import au.ellie.hyui.utils.MultiHudWrapper;
import au.ellie.hyui.utils.PngDownloadUtils;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.protocol.Asset;
import com.hypixel.hytale.protocol.Packet;
import com.hypixel.hytale.protocol.packets.setup.AssetFinalize;
import com.hypixel.hytale.protocol.packets.setup.AssetInitialize;
import com.hypixel.hytale.protocol.packets.setup.AssetPart;
import com.hypixel.hytale.protocol.packets.setup.RequestCommonAssetsRebuild;
import com.hypixel.hytale.server.core.asset.common.events.SendCommonAssetsEvent;
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.PageManager;
import com.hypixel.hytale.server.core.event.events.player.PlayerReadyEvent;
import com.hypixel.hytale.server.core.io.PacketHandler;
Expand All @@ -46,46 +38,46 @@
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
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 javax.annotation.Nonnull;
import java.io.IOException;
import java.util.*;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentMap;

public class HyUIPlugin extends JavaPlugin {

private static HyUIPluginLogger instance;
private static HyUIPluginLogger logger;

private static final boolean ADD_CMDS = false;

private static final ConcurrentMap<PlayerRef, Deque<Asset>> PENDING_ASSETS =
new ConcurrentHashMap<>();
private static final ConcurrentMap<PlayerRef, Boolean> REBUILD_SCHEDULED =
new ConcurrentHashMap<>();

public static HyUIPluginLogger getLog() {
if (instance == null)
instance = new HyUIPluginLogger();
return instance;
if (logger == null)
logger = new HyUIPluginLogger();

return logger;
}

public HyUIPlugin(@Nonnull JavaPluginInit init) {
super(init);
if (instance == null)
instance = new HyUIPluginLogger();
}

@Override
protected void setup() {
// Intercept: AssetFinalize, RequestCommonAssetsRebuild, AssetPart, AssetInitialize
PacketAdapters.registerOutbound((PacketHandler handler, Packet packet) -> {
var packetName = packet.getClass().getSimpleName();
if (!(handler instanceof GamePacketHandler h)) {
if (!(handler instanceof GamePacketHandler h))
return;
}

switch (packetName) {
case "RequestCommonAssetsRebuild": {
var pRef = h.getPlayerRef();
Expand All @@ -99,11 +91,10 @@ protected void setup() {
break;
}
}

});

if (ADD_CMDS) {
instance.logFinest("Setting up plugin " + this.getName());
getLog().logFinest("Setting up plugin " + this.getName());
this.getCommandRegistry().registerCommand(new HyUITestGuiCommand());
this.getCommandRegistry().registerCommand(new HyUIAddHudCommand());
this.getCommandRegistry().registerCommand(new HyUIRemHudCommand());
Expand All @@ -112,34 +103,33 @@ protected void setup() {
this.getCommandRegistry().registerCommand(new HyUITemplateRuntimeCommand());
this.getCommandRegistry().registerCommand(new HyUIBountyCommand());
this.getCommandRegistry().registerCommand(new HyUITabsCommand());

this.getEventRegistry().registerGlobal(PlayerReadyEvent.class, event -> {
instance.logFinest("Player ready event triggered for " + event.getPlayer().getDisplayName());

var player = event.getPlayer();
if (player == null) return;
getLog().logFinest("Player ready event triggered for " + event.getPlayer().getDisplayName());

Ref<EntityStore> ref = event.getPlayerRef();
var player = event.getPlayer();
var ref = event.getPlayerRef();
if (!ref.isValid()) return;

Store<EntityStore> store = ref.getStore();
World world = store.getExternalData().getWorld();
var store = ref.getStore();
var world = store.getExternalData().getWorld();

world.execute(() -> {
PlayerRef playerRef = store.getComponent(ref, PlayerRef.getComponentType());
var playerRef = store.getComponent(ref, PlayerRef.getComponentType());
try {
PngDownloadUtils.prefetchPngForPlayer(playerRef, HyvatarUtils.buildRenderUrl(
player.getDisplayName(),
HyvatarUtils.RenderType.HEAD,
player.getDisplayName(),
HyvatarUtils.RenderType.HEAD,
64, null, null), 18000);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
String html = "Pages/HudTest.html";

var html = "Pages/HudTest.html";
var tp = new TemplateProcessor();
tp.setVariable("playerName", player.getDisplayName());
var hud = HudBuilder.detachedHud()

HudBuilder.detachedHud()
.loadHtml(html, tp)
.withRefreshRate(1000)
.onRefresh((h) -> {
Expand All @@ -152,19 +142,18 @@ protected void setup() {

});
}

}

private static void enqueueAsset(PlayerRef playerRef, Asset asset) {
if (playerRef == null || asset == null) {
if (playerRef == null || asset == null)
return;
}
PENDING_ASSETS.computeIfAbsent(playerRef, ref -> new ConcurrentLinkedDeque<>()).push(asset);

PENDING_ASSETS.computeIfAbsent(playerRef, _ -> new ConcurrentLinkedDeque<>()).push(asset);
}

private static void scheduleReopenAfterRebuild(PlayerRef playerRef) {
if (playerRef == null || !playerRef.isValid()
|| playerRef.getReference() == null
if (playerRef == null || !playerRef.isValid()
|| playerRef.getReference() == null
|| !playerRef.getReference().isValid()) {
return;
}
Expand Down
13 changes: 8 additions & 5 deletions src/main/java/au/ellie/hyui/HyUIPluginLogger.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,18 @@
import com.hypixel.hytale.logger.HytaleLogger;

public class HyUIPluginLogger {

private final HytaleLogger internalLogger = HytaleLogger.forEnclosingClass();


public static final boolean IS_DEV = "true".equals(System.getenv("HYUI_DEV"));
private final HytaleLogger internalLogger = HytaleLogger.forEnclosingClass();

public HyUIPluginLogger() {

}


public void logWarn(String message) {
internalLogger.atWarning().log(message);
}

public void logFinest(String message) {
internalLogger.atFinest().log(message);
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/au/ellie/hyui/assets/DynamicImageAsset.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ protected CompletableFuture<byte[]> getBlob0() {
public static void sendToPlayer(PacketHandler handler, CommonAsset asset) {
byte[] allBytes = asset.getBlob().join();
byte[][] parts = ArrayUtil.split(allBytes, 2621440);

ToClientPacket[] packets = new ToClientPacket[1 + parts.length];
packets[0] = new AssetInitialize(asset.toPacket(), allBytes.length);

Expand Down
39 changes: 20 additions & 19 deletions src/main/java/au/ellie/hyui/builders/ActionButtonBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
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 java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
Expand Down Expand Up @@ -114,98 +115,98 @@ public ActionButtonBuilder withActionName(String actionName) {
* Adds an event listener for the Activating event.
*/
public ActionButtonBuilder onActivating(Runnable callback) {
return addEventListener(CustomUIEventBindingType.Activating, Void.class, v -> callback.run());
return addEventListener(CustomUIEventBindingType.Activating, callback);
}

/**
* Adds an event listener for the Activating event with context.
*/
public ActionButtonBuilder onActivating(BiConsumer<Void, UIContext> callback) {
return addEventListenerWithContext(CustomUIEventBindingType.Activating, Void.class, callback);
return addEventListenerWithContext(CustomUIEventBindingType.Activating, callback);
}

/**
* Adds an event listener for the DoubleClicking event.
*/
public ActionButtonBuilder onDoubleClicking(Runnable callback) {
return addEventListener(CustomUIEventBindingType.DoubleClicking, MouseEventData.class, v -> callback.run());
return addEventListener(CustomUIEventBindingType.DoubleClicking, callback);
}

/**
* Adds an event listener for the DoubleClicking event.
*/
public ActionButtonBuilder onDoubleClicking(Consumer<MouseEventData> callback) {
return addEventListener(CustomUIEventBindingType.DoubleClicking, MouseEventData.class, callback);
return addEventListenerWithContext(CustomUIEventBindingType.DoubleClicking, callback);
}

/**
* Adds an event listener for the DoubleClicking event with context.
*/
public ActionButtonBuilder onDoubleClicking(BiConsumer<MouseEventData, UIContext> callback) {
return addEventListenerWithContext(CustomUIEventBindingType.DoubleClicking, MouseEventData.class, callback);
return addEventListenerWithContext(CustomUIEventBindingType.DoubleClicking, callback);
}

/**
* Adds an event listener for the RightClicking event.
*/
public ActionButtonBuilder onRightClicking(Runnable callback) {
return addEventListener(CustomUIEventBindingType.RightClicking, MouseEventData.class, v -> callback.run());
return addEventListener(CustomUIEventBindingType.RightClicking, callback);
}

/**
* Adds an event listener for the RightClicking event.
*/
public ActionButtonBuilder onRightClicking(Consumer<MouseEventData> callback) {
return addEventListener(CustomUIEventBindingType.RightClicking, MouseEventData.class, callback);
return addEventListenerWithContext(CustomUIEventBindingType.RightClicking, callback);
}

/**
* Adds an event listener for the RightClicking event with context.
*/
public ActionButtonBuilder onRightClicking(BiConsumer<MouseEventData, UIContext> callback) {
return addEventListenerWithContext(CustomUIEventBindingType.RightClicking, MouseEventData.class, callback);
return addEventListenerWithContext(CustomUIEventBindingType.RightClicking, callback);
}

/**
* Adds an event listener for the MouseEntered event.
*/
public ActionButtonBuilder onMouseEntered(Runnable callback) {
return addEventListener(CustomUIEventBindingType.MouseEntered, MouseEventData.class, v -> callback.run());
return addEventListener(CustomUIEventBindingType.MouseEntered, callback);
}

/**
* Adds an event listener for the MouseEntered event.
*/
public ActionButtonBuilder onMouseEntered(Consumer<MouseEventData> callback) {
return addEventListener(CustomUIEventBindingType.MouseEntered, MouseEventData.class, callback);
return addEventListenerWithContext(CustomUIEventBindingType.MouseEntered, callback);
}

/**
* Adds an event listener for the MouseEntered event with context.
*/
public ActionButtonBuilder onMouseEntered(BiConsumer<MouseEventData, UIContext> callback) {
return addEventListenerWithContext(CustomUIEventBindingType.MouseEntered, MouseEventData.class, callback);
return addEventListenerWithContext(CustomUIEventBindingType.MouseEntered, callback);
}

/**
* Adds an event listener for the MouseExited event.
*/
public ActionButtonBuilder onMouseExited(Runnable callback) {
return addEventListener(CustomUIEventBindingType.MouseExited, MouseEventData.class, v -> callback.run());
return addEventListener(CustomUIEventBindingType.MouseExited, callback);
}

/**
* Adds an event listener for the MouseExited event.
*/
public ActionButtonBuilder onMouseExited(Consumer<MouseEventData> callback) {
return addEventListener(CustomUIEventBindingType.MouseExited, MouseEventData.class, callback);
return addEventListenerWithContext(CustomUIEventBindingType.MouseExited, callback);
}

/**
* Adds an event listener for the MouseExited event with context.
*/
public ActionButtonBuilder onMouseExited(BiConsumer<MouseEventData, UIContext> callback) {
return addEventListenerWithContext(CustomUIEventBindingType.MouseExited, MouseEventData.class, callback);
return addEventListenerWithContext(CustomUIEventBindingType.MouseExited, callback);
}

@Override
Expand Down Expand Up @@ -283,27 +284,27 @@ protected void onBuild(UICommandBuilder commands, UIEventBuilder events) {
HyUIPlugin.getLog().logFinest("Adding Activating event binding: " + eventId + " for " + selector);
events.addEventBinding(CustomUIEventBindingType.Activating, selector,
EventData.of("Action", UIEventActions.BUTTON_CLICKED)
.append("Target", eventId), false);
.append("Target", eventId), false);
} else if (listener.type() == CustomUIEventBindingType.DoubleClicking) {
HyUIPlugin.getLog().logFinest("Adding DoubleClicking event binding for " + selector);
events.addEventBinding(CustomUIEventBindingType.DoubleClicking, selector,
EventData.of("Action", UIEventActions.DOUBLE_CLICKING)
.append("Target", eventId), false);
.append("Target", eventId), false);
} else if (listener.type() == CustomUIEventBindingType.RightClicking) {
HyUIPlugin.getLog().logFinest("Adding RightClicking event binding for " + selector);
events.addEventBinding(CustomUIEventBindingType.RightClicking, selector,
EventData.of("Action", UIEventActions.RIGHT_CLICKING)
.append("Target", eventId), false);
.append("Target", eventId), false);
} else if (listener.type() == CustomUIEventBindingType.MouseEntered) {
HyUIPlugin.getLog().logFinest("Adding MouseEntered event binding for " + selector);
events.addEventBinding(CustomUIEventBindingType.MouseEntered, selector,
EventData.of("Action", UIEventActions.MOUSE_ENTERED)
.append("Target", eventId), false);
.append("Target", eventId), false);
} else if (listener.type() == CustomUIEventBindingType.MouseExited) {
HyUIPlugin.getLog().logFinest("Adding MouseExited event binding for " + selector);
events.addEventBinding(CustomUIEventBindingType.MouseExited, selector,
EventData.of("Action", UIEventActions.MOUSE_EXITED)
.append("Target", eventId), false);
.append("Target", eventId), false);
}
});
}
Expand Down
Loading