Files
Documentation/content/ui-systems/custom-pages.fr.md
2026-01-20 20:33:59 +01:00

9.9 KiB

title, type, weight
title type weight
Pages Personnalisées docs 2

Les pages personnalisées permettent aux plugins de créer des overlays UI interactifs qui peuvent recevoir des entrées utilisateur et afficher du contenu dynamique.

Package: com.hypixel.hytale.server.core.entity.entities.player.pages

Durées de Vie des Pages

Contrôle comment les pages peuvent être fermées :

Durée de Vie Description
CustomPageLifetime.CantClose La page ne peut pas être fermée par le joueur
CustomPageLifetime.CanDismiss Le joueur peut fermer avec Échap
CustomPageLifetime.CanDismissOrCloseThroughInteraction Peut fermer via Échap ou interaction

Créer une Page Personnalisée

Étendez CustomUIPage :

public 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) {
        // Charger un template UI depuis les assets (extension .ui OBLIGATOIRE)
        builder.append("Pages/WelcomeScreen.ui");

        // Définir des valeurs dynamiques
        builder.set("#player_name", playerRef.getUsername());
        builder.set("#server_name", "Mon Serveur");
    }
}

UICommandBuilder

Construire du contenu UI dynamiquement :

Ajouter du Contenu

// Ajouter un template UI (extension .ui OBLIGATOIRE)
builder.append("Pages/TemplatePath.ui");

// Ajouter à un sélecteur spécifique
builder.append("#container", "Pages/ChildTemplate.ui");

// Ajouter du YAML/JSON inline
builder.appendInline("#list", "{ type: text, content: 'Bonjour' }");

Définir des Valeurs

// Définir du texte
builder.set("#title", "Bienvenue !");
builder.set("#description", Message.translation("my.key"));

// Définir des nombres
builder.set("#score", 1500);
builder.set("#progress", 0.75f);

// Définir un booléen
builder.set("#visible", true);

// Mettre à null
builder.setNull("#optional_field");

Définir des Objets

// Définir un ItemStack
ItemStack item = ...;
builder.setObject("#item_display", item);

// Définir une Zone UI
Area area = new Area(0, 0, 100, 50);
builder.setObject("#bounds", area);

// Définir des tableaux/listes
builder.set("#items", itemStackArray);
builder.set("#options", stringList);

Supprimer du Contenu

// Vider les enfants d'un élément
builder.clear("#container");

// Supprimer un élément entièrement
builder.remove("#element_id");

// Insérer avant un élément
builder.insertBefore("#existing", "Pages/NewElement.ui");

UIEventBuilder

Lier des événements aux éléments UI avec addEventBinding :

import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType;
import com.hypixel.hytale.server.core.ui.builder.EventData;

@Override
public void build(Ref<EntityStore> ref, UICommandBuilder builder,
                  UIEventBuilder events, Store<EntityStore> store) {
    builder.append("Pages/Menu.ui");

    // Lier des événements de clic avec addEventBinding
    events.addEventBinding(CustomUIEventBindingType.Activating, "#button_start",
        EventData.of("Action", "start_game"));
    events.addEventBinding(CustomUIEventBindingType.Activating, "#button_settings",
        EventData.of("Action", "open_settings"));
}

Types de Liaison d'Événements

Type Description
Activating Clic/activation de bouton
RightClicking Action clic droit
DoubleClicking Action double-clic
MouseEntered Entrée du survol souris
MouseExited Sortie du survol souris
ValueChanged Valeur d'entrée changée
FocusGained Élément focus
FocusLost Élément perd le focus

Gérer les Événements

Surchargez handleDataEvent pour traiter les interactions utilisateur. Le paramètre rawData contient du JSON avec les données de l'événement :

public 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("Pages/MainMenu.ui");

        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 contient du JSON comme : {"Action":"play"}
        if (rawData.contains("\"Action\":\"play\"")) {
            startGame();
        } else if (rawData.contains("\"Action\":\"quit\"")) {
            close();
        }
    }

    private void startGame() {
        // Logique de jeu...
        close();  // Fermer la page
    }
}

{{< callout type="info" >}} Conseil : Pour une gestion d'événements plus complexe, envisagez d'utiliser une bibliothèque de parsing JSON ou d'étendre InteractiveCustomUIPage<T> avec une classe de données typée. {{< /callout >}}

Ouvrir des Pages

Utilisez PageManager pour ouvrir des pages :

Player player = ...;
PageManager pageManager = player.getPageManager();

// Ouvrir une page personnalisée
pageManager.openCustomPage(
    player.getReference(),
    player.getReference().getStore(),
    new MenuPage(player.getPlayerRef())
);

Fermer des Pages

// Depuis l'intérieur de la page
protected void close() {
    // Méthode héritée - ferme cette page
}

// Depuis l'extérieur
pageManager.setPage(ref, store, Page.None);

Mettre à Jour des Pages

Mettre à jour le contenu sans reconstruire :

// Envoyer une mise à jour avec de nouvelles commandes
UICommandBuilder update = new UICommandBuilder();
update.set("#score", newScore);
sendUpdate(update);

// Reconstruction complète
rebuild();

// Vider et mettre à jour
sendUpdate(update, true);  // clear=true

Page avec Fenêtres

Ouvrir des pages avec des fenêtres d'inventaire :

// Ouvrir une page avec des fenêtres
pageManager.openCustomPageWithWindows(
    ref, store,
    new CraftingPage(playerRef),
    craftingWindow,
    playerInventoryWindow
);

Exemple de Page Interactive

public 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("Pages/Shop.ui");

        // Peupler les 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) {
        // Parser les données JSON de l'événement
        if (rawData.contains("\"Action\":\"select\"")) {
            // Extraire l'index du 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) {
        // Logique d'achat...
        rebuild();  // Rafraîchir l'UI
    }
}

Pages Intégrées

Types de pages disponibles :

Page Description
Page.None Pas de page ouverte (fermer la page courante)
Page.Bench Interface d'établi/crafting
Page.Inventory Inventaire du joueur
Page.ToolsSettings Paramètres des outils
Page.Map Carte du monde
Page.MachinimaEditor Éditeur de machinima
Page.ContentCreation Outils de création de contenu
Page.Custom Page UI personnalisée
// Ouvrir une page intégrée
pageManager.setPage(ref, store, Page.Inventory);

Bonnes Pratiques

{{< callout type="info" >}} Directives des Pages Personnalisées :

  • Utilisez CanDismiss sauf si la page doit être modale
  • Gérez le callback onDismiss pour le nettoyage
  • Utilisez sendUpdate() pour les petits changements, rebuild() pour les changements majeurs
  • Stockez l'état de la page dans des champs d'instance
  • Fermez correctement les pages en utilisant close() ou setPage(Page.None) {{< /callout >}}

{{< callout type="warning" >}} Important : Vérifiez toujours si playerRef.getReference() est null avant d'accéder au store, car le joueur peut s'être déconnecté. {{< /callout >}}