Skip to content

Commit 8644894

Browse files
committed
feat: dynamic title update Component support
1 parent 8bbdd72 commit 8644894

17 files changed

Lines changed: 165 additions & 27 deletions

File tree

inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewContainer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public interface ViewContainer {
8181

8282
void close(@NotNull Viewer viewer);
8383

84-
void changeTitle(@Nullable String title, @NotNull Viewer target);
84+
void changeTitle(@Nullable Object title, @NotNull Viewer target);
8585

8686
boolean isEntityContainer();
8787

inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFConfinedContext.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,33 @@ public interface IFConfinedContext extends IFContext {
5656
* library developers to add support to your version.
5757
*
5858
* @param title The new container title.
59+
* @see <a href="https://github.com/DevNatan/inventory-framework/wiki/navigating-between-views">Navigating between Views on Wiki</a>
5960
*/
6061
void updateTitleForPlayer(@NotNull String title);
6162

63+
/**
64+
* Updates the container title only for the player current scope of execution.
65+
*
66+
* <p>This should not be used before the container is opened, if you need to set the __initial
67+
* title__ use {@link IFOpenContext#modifyConfig()} on open handler instead.
68+
*
69+
* <p>This method is version dependant, so it may be that your server version is not yet
70+
* supported, if you try to use this method and fail (can fail silently), report it to the
71+
* library developers to add support to your version.
72+
* <p>
73+
*
74+
* <a href="https://github.com/KyoriPowered/adventure">Kyori's Adventure Text Component</a> is supported if your platform is PaperSpigot
75+
* in a non-legacy version. Non-{@link String} titles will be converted to a plain text.
76+
* <p>
77+
* <b><i> This API is experimental and is not subject to the general compatibility guarantees
78+
* such API may be changed or may be removed completely in any further release. </i></b>
79+
*
80+
* @param title The new container title.
81+
* @see <a href="https://github.com/DevNatan/inventory-framework/wiki/dynamic-title-update">Dynamic Title Update on Wiki</a>
82+
*/
83+
@ApiStatus.Experimental
84+
void updateTitleForPlayer(@NotNull Object title);
85+
6286
/**
6387
* Resets the container title only for the player current scope of execution to the initially
6488
* defined title. Must be used after {@link #updateTitleForPlayer(String)} to take effect.

inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFContext.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,27 @@ void performClickInComponent(
212212
*/
213213
void updateTitleForEveryone(@NotNull String title);
214214

215+
/**
216+
* Updates the container title for everyone that's viewing it.
217+
*
218+
* <p>This should not be used before the container is opened, if you need to set the __initial
219+
* title__ use {@link IFOpenContext#modifyConfig()} on open handler instead.
220+
*
221+
* <p>This method is version dependant, so it may be that your server version is not yet
222+
* supported, if you try to use this method and fail (can fail silently), report it to the
223+
* library developers to add support to your version.
224+
* <p>
225+
* <a href="https://github.com/KyoriPowered/adventure">Kyori's Adventure Text Component</a> is supported if your platform is PaperSpigot
226+
* in a non-legacy version. Non-{@link String} titles will be converted to a plain text.
227+
* <p>
228+
* <b><i> This API is experimental and is not subject to the general compatibility guarantees
229+
* such API may be changed or may be removed completely in any further release. </i></b>
230+
*
231+
* @param title The new container title.
232+
*/
233+
@ApiStatus.Experimental
234+
void updateTitleForEveryone(@NotNull Object title);
235+
215236
/**
216237
* Updates the container title to all viewers in this context, to the initially defined title.
217238
* Must be used after {@link #updateTitleForEveryone(String)} to take effect.

inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewContainer.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import java.util.ArrayList;
44
import java.util.Objects;
5-
import me.devnatan.inventoryframework.runtime.thirdparty.InventoryUpdate;
5+
import me.devnatan.inventoryframework.internal.InventoryFactory;
66
import org.bukkit.entity.HumanEntity;
77
import org.bukkit.entity.Player;
88
import org.bukkit.inventory.Inventory;
@@ -147,12 +147,12 @@ public int getLastSlot() {
147147
}
148148

149149
@Override
150-
public void changeTitle(@Nullable String title, @NotNull Viewer target) {
150+
public void changeTitle(@Nullable Object title, @NotNull Viewer target) {
151151
changeTitle(title, ((BukkitViewer) target).getPlayer());
152152
}
153153

154-
public void changeTitle(@Nullable String title, @NotNull Player target) {
155-
InventoryUpdate.updateInventory(target, title);
154+
public void changeTitle(@Nullable Object title, @NotNull Player target) {
155+
InventoryFactory.current().setInventoryTitle(target, title);
156156
}
157157

158158
@Override

inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/SlotClickContext.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,11 @@ public final void updateTitleForPlayer(@NotNull String title) {
162162
getParent().updateTitleForPlayer(title);
163163
}
164164

165+
@Override
166+
public final void updateTitleForPlayer(@NotNull Object title) {
167+
getParent().updateTitleForPlayer(title);
168+
}
169+
165170
@Override
166171
public final void resetTitleForPlayer() {
167172
getParent().resetTitleForPlayer();

inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/SlotRenderContext.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ public final void updateTitleForPlayer(@NotNull String title) {
119119
getParent().updateTitleForPlayer(title);
120120
}
121121

122+
@Override
123+
public final void updateTitleForPlayer(@NotNull Object title) {
124+
getParent().updateTitleForPlayer(title);
125+
}
126+
122127
@Override
123128
public final void resetTitleForPlayer() {
124129
getParent().resetTitleForPlayer();

inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/internal/BukkitInventoryFactory.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import static me.devnatan.inventoryframework.runtime.util.InventoryUtils.toInventoryType;
55

66
import me.devnatan.inventoryframework.ViewType;
7+
import me.devnatan.inventoryframework.runtime.thirdparty.InventoryUpdate;
78
import org.bukkit.Bukkit;
9+
import org.bukkit.entity.Player;
810
import org.bukkit.inventory.Inventory;
911
import org.bukkit.inventory.InventoryHolder;
1012

@@ -30,4 +32,13 @@ public Inventory createInventory(InventoryHolder holder, ViewType type, int size
3032
}
3133
return inventory;
3234
}
35+
36+
@Override
37+
public void setInventoryTitle(Player player, Object newTitle) {
38+
if (!(newTitle instanceof String))
39+
throw new UnsupportedOperationException(
40+
"Import inventory-framework-platform-paper to use Component as inventory title.");
41+
42+
InventoryUpdate.updateInventory(player, newTitle);
43+
}
3344
}

inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/internal/InventoryFactory.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,17 @@
22

33
import me.devnatan.inventoryframework.IFDebug;
44
import me.devnatan.inventoryframework.ViewType;
5+
import org.bukkit.entity.Player;
56
import org.bukkit.inventory.Inventory;
67
import org.bukkit.inventory.InventoryHolder;
8+
import org.jetbrains.annotations.ApiStatus;
79

8-
abstract class InventoryFactory {
10+
/**
11+
* <b><i> This is an internal inventory-framework API that should not be used from outside of
12+
* this library. No compatibility guarantees are provided. </i></b>
13+
*/
14+
@ApiStatus.Internal
15+
public abstract class InventoryFactory {
916

1017
protected static InventoryFactory instance;
1118

@@ -35,4 +42,6 @@ private static InventoryFactory forCurrentPlatform() {
3542
}
3643

3744
public abstract Inventory createInventory(InventoryHolder holder, ViewType type, int size, Object title);
45+
46+
public abstract void setInventoryTitle(Player player, Object newTitle);
3847
}

inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/runtime/thirdparty/InventoryUpdate.java

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public final class InventoryUpdate {
6666

6767
private static final Set<String> UNOPENABLES = Sets.newHashSet("CRAFTING", "CREATIVE", "PLAYER");
6868
private static final boolean SUPPORTS_19 = McVersion.supports(19);
69-
private static final Object[] DUMMY_COLOR_MODIFIERS = new Object[0];
69+
public static final Object[] DUMMY_COLOR_MODIFIERS = new Object[0];
7070

7171
static {
7272
// Initialize classes.
@@ -86,6 +86,10 @@ public final class InventoryUpdate {
8686
I_CHAT_BASE_COMPONENT, "b", MethodType.methodType(I_CHAT_MUTABLE_COMPONENT, String.class), true)
8787
: null;
8888

89+
System.out.println("SUPPORTS_19 = " + SUPPORTS_19);
90+
System.out.println("CHAT_MESSAGE = " + CHAT_MESSAGE);
91+
System.out.println("literal = " + literal);
92+
8993
// Initialize constructors.
9094
chatMessage = SUPPORTS_19 ? null : getConstructor(CHAT_MESSAGE, String.class, Object[].class);
9195
packetPlayOutOpenWindow = (useContainers())
@@ -107,30 +111,27 @@ public final class InventoryUpdate {
107111
* @param newTitle the new title for the inventory.
108112
*/
109113
@SuppressWarnings("UnstableApiUsage")
110-
public static void updateInventory(Player player, String newTitle) {
114+
public static void updateInventory(Player player, Object newTitle) {
111115
Preconditions.checkArgument(player != null, "Cannot update inventory to null player.");
112116

113117
if (newTitle == null) newTitle = "";
114118

115119
try {
116-
if (newTitle.length() > 32) {
117-
newTitle = newTitle.substring(0, 32);
120+
if (newTitle instanceof String && ((String) newTitle).length() > 32) {
121+
newTitle = ((String) newTitle).substring(0, 32);
118122
}
119123

120-
if (McVersion.supports(20)) {
124+
if (newTitle instanceof String && McVersion.supports(20)) {
121125
InventoryView open = player.getOpenInventory();
122126
if (UNOPENABLES.contains(open.getType().name())) return;
123-
open.setTitle(newTitle);
127+
open.setTitle((String) newTitle);
124128
return;
125129
}
126130

127131
// Get EntityPlayer from CraftPlayer.
128132
Object craftPlayer = CRAFT_PLAYER.cast(player);
129133
Object entityPlayer = GET_HANDLE.invoke(craftPlayer);
130134

131-
// Create new title.
132-
Object title = createTitleComponent(newTitle);
133-
134135
// Get activeContainer from EntityPlayer.
135136
Object activeContainer = getActiveContainer(entityPlayer);
136137

@@ -162,12 +163,31 @@ public static void updateInventory(Player player, String newTitle) {
162163
return;
163164
}
164165

165-
Object object = getContainerOrName(container, type);
166-
167-
// Create packet.
168-
Object packet = useContainers()
169-
? packetPlayOutOpenWindow.invoke(windowId, object, title)
170-
: packetPlayOutOpenWindow.invoke(windowId, object, title, size);
166+
Object packet;
167+
if (!(newTitle instanceof String)) {
168+
final Class<?> menuTypeClass = Class.forName("net.minecraft.world.inventory.MenuType");
169+
final Class<?> paperAdventure = Class.forName("io.papermc.paper.adventure.PaperAdventure");
170+
final Class<?> componentClass = Class.forName("net.kyori.adventure.text.Component");
171+
final Object minecraftComponent =
172+
paperAdventure.getMethod("asVanilla", componentClass).invoke(null, newTitle);
173+
174+
// Bukkit uses uppercase "X", Minecraft uses lowercase "x"
175+
// Bukkit: GENERIC_9X3 / Minecraft: GENERIC_9x3
176+
final String minecraftEnumName = container.name().replace("X", "x");
177+
178+
final Object menuType =
179+
menuTypeClass.getField(minecraftEnumName).get(null);
180+
181+
packet = packetPlayOutOpenWindow.invoke(windowId, menuType, minecraftComponent);
182+
} else {
183+
// Create new title.
184+
Object title = createTitleComponent(newTitle);
185+
Object object = getContainerOrName(container, type);
186+
187+
packet = useContainers()
188+
? packetPlayOutOpenWindow.invoke(windowId, object, title)
189+
: packetPlayOutOpenWindow.invoke(windowId, object, title, size);
190+
}
171191

172192
// Send packet sync.
173193
ReflectionUtils.sendPacketSync(player, packet);
@@ -391,9 +411,11 @@ public Object getObject() {
391411
int version = ReflectionUtils.MINOR_NUMBER;
392412
String name = (version == 14 && this == CARTOGRAPHY_TABLE) ? "CARTOGRAPHY" : name();
393413
// Since 1.17, containers go from "a" to "x".
394-
if (version > 16 && version <= 20) name = String.valueOf(alphabet[ordinal()]);
414+
if (version > 16 && version <= 20) name = java.lang.String.valueOf(alphabet[ordinal()]);
415+
395416
Field field = CONTAINERS.getField(name);
396417
return field.get(null);
418+
397419
} catch (ReflectiveOperationException exception) {
398420
exception.printStackTrace();
399421
}

inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/MinestomViewContainer.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,15 @@ class MinestomViewContainer(
105105
}
106106

107107
override fun changeTitle(
108-
title: String?,
108+
title: Any?,
109109
target: Viewer,
110110
) {
111-
changeTitle(
112-
title?.let { Component.text(it) } ?: Component.empty(),
113-
(target as MinestomViewer).player,
114-
)
111+
val newTitle =
112+
title as? Component
113+
?: title?.let { raw -> Component.text(raw as String) }
114+
?: Component.empty()
115+
116+
changeTitle(newTitle, (target as MinestomViewer).player)
115117
}
116118

117119
fun changeTitle(

0 commit comments

Comments
 (0)