2222import com .loohp .interactionvisualizer .objectholders .EntryKey ;
2323import com .loohp .interactionvisualizer .utils .ChatColorUtils ;
2424import com .loohp .interactionvisualizer .utils .ItemNameUtils ;
25- import com .loohp .interactionvisualizer .utils .LegacyTextComponentCache ;
2625import com .loohp .interactionvisualizer .scheduler .ScheduledRunnable ;
2726import com .loohp .interactionvisualizer .scheduler .ScheduledTask ;
2827import com .loohp .interactionvisualizer .scheduler .Scheduler ;
2928import net .kyori .adventure .text .Component ;
30- import net .kyori .adventure .text .TextReplacementConfig ;
3129import net .kyori .adventure .text .format .NamedTextColor ;
3230import net .kyori .adventure .text .serializer .plain .PlainTextComponentSerializer ;
3331import org .bukkit .Bukkit ;
@@ -93,12 +91,10 @@ public final class DroppedItemDisplay extends VisualizerRunnableDisplay implemen
9391 private final DroppedItemSpatialIndex crampIndex = new DroppedItemSpatialIndex ();
9492 private final NamespacedKey visualEntityKey ;
9593
96- private String regularFormatting ;
97- private String singularFormatting ;
98- private String toolsFormatting ;
9994 private String highColor = "" ;
10095 private String mediumColor = "" ;
10196 private String lowColor = "" ;
97+ private DroppedItemLabelFormatter labelFormatter ;
10298 private int cramp = 6 ;
10399 private double labelYOffset = 0.8D ;
104100 private int updateRate = 20 ;
@@ -119,12 +115,15 @@ public DroppedItemDisplay() {
119115 @ EventHandler
120116 public void onReload (InteractionVisualizerReloadEvent event ) {
121117 DroppedItemVisibilityPolicy previousVisibilityPolicy = visibilityPolicy ;
122- regularFormatting = configString ("Entities.Item.Options.RegularFormat" );
123- singularFormatting = configString ("Entities.Item.Options.SingularFormat" );
124- toolsFormatting = configString ("Entities.Item.Options.ToolsFormat" );
118+ String regularFormatting = configString ("Entities.Item.Options.RegularFormat" );
119+ String singularFormatting = configString ("Entities.Item.Options.SingularFormat" );
120+ String toolsFormatting = configString ("Entities.Item.Options.ToolsFormat" );
125121 highColor = configString ("Entities.Item.Options.Color.High" );
126122 mediumColor = configString ("Entities.Item.Options.Color.Medium" );
127123 lowColor = configString ("Entities.Item.Options.Color.Low" );
124+ labelFormatter = new DroppedItemLabelFormatter (
125+ regularFormatting , singularFormatting , toolsFormatting ,
126+ highColor , mediumColor , lowColor );
128127 cramp = InteractionVisualizer .plugin .getConfiguration ().getInt ("Entities.Item.Options.Cramping" );
129128 double configuredLabelYOffset = InteractionVisualizer .plugin .getConfiguration ()
130129 .getDouble ("Entities.Item.Options.LabelYOffset" );
@@ -577,16 +576,22 @@ private void update(UUID itemId, Item item, DroppedItemSpatialIndex itemIndex) {
577576 return ;
578577 }
579578
580- Component text = format (content , ticksLeft );
579+ Component text = labelFormatter . format (content . formatState , ticksLeft );
581580 boolean created = false ;
582581 if (label == null || !label .isValid () || !label .getWorld ().equals (item .getWorld ())) {
583582 removeLabel (itemId );
584583 label = spawnLabel (item );
585584 labels .put (itemId , label );
586585 created = true ;
587586 }
588- if (!text .equals (label .text ())) {
587+ // The formatter returns the same immutable component instance while
588+ // the rendered state is unchanged. Avoid a CraftTextDisplay read on
589+ // every refresh; it serializes the NMS component back through Paper.
590+ int labelId = label .getEntityId ();
591+ if (content .appliedLabelId != labelId || content .appliedText != text ) {
589592 label .text (text );
593+ content .appliedLabelId = labelId ;
594+ content .appliedText = text ;
590595 }
591596 float targetViewRange = labelViewRange ();
592597 if (Math .abs (label .getViewRange () - targetViewRange ) > 1.0E-4F ) {
@@ -912,42 +917,16 @@ private CachedItemContent cachedContent(UUID itemId, ItemStack stack) {
912917 NamespacedKey customItemId = blacklist .requiresCustomItemId ()
913918 ? CustomContentManager .customItemId (stack ).orElse (null )
914919 : null ;
920+ String durability = durability (stack );
921+ Component itemName = ItemNameUtils .getDisplayName (stack );
915922 CachedItemContent replacement = new CachedItemContent (
916923 stack .clone (),
917924 blacklist .matches (matchingName , stack .getType (), customItemId ),
918- durability (stack ),
919- ItemNameUtils .getDisplayName (stack ));
925+ labelFormatter .state (stack .getAmount (), durability , itemName ));
920926 contentCache .put (itemId , replacement );
921927 return replacement ;
922928 }
923929
924- private Component format (CachedItemContent content , int ticksLeft ) {
925- int secondsLeft = Math .max (0 , ticksLeft / 20 );
926- if (content .lastSeconds == secondsLeft && content .lastFormatted != null ) {
927- return content .lastFormatted ;
928- }
929- ItemStack stack = content .stack ;
930- int amount = stack .getAmount ();
931- String timerColor = secondsLeft <= 30 ? lowColor : secondsLeft <= 120 ? mediumColor : highColor ;
932- String timer = timerColor + String .format (java .util .Locale .ROOT , "%02d:%02d" , secondsLeft / 60 , secondsLeft % 60 );
933-
934- String template ;
935- if (ticksLeft >= 600 && content .durability != null ) {
936- template = toolsFormatting .replace ("{Durability}" , content .durability );
937- } else {
938- template = amount == 1 ? singularFormatting : regularFormatting ;
939- }
940- String rendered = template .replace ("{Amount}" , Integer .toString (amount )).replace ("{Timer}" , timer );
941- Component component = LegacyTextComponentCache .parse (rendered );
942- Component formatted = component .replaceText (TextReplacementConfig .builder ()
943- .matchLiteral ("{Item}" )
944- .replacement (content .itemName )
945- .build ());
946- content .lastSeconds = secondsLeft ;
947- content .lastFormatted = formatted ;
948- return formatted ;
949- }
950-
951930 private String durability (ItemStack stack ) {
952931 if (stack .getType ().getMaxDurability () <= 0 || !(stack .getItemMeta () instanceof Damageable damageable )) {
953932 return null ;
@@ -1055,17 +1034,15 @@ private static final class CachedItemContent {
10551034
10561035 private final ItemStack stack ;
10571036 private final boolean blacklisted ;
1058- private final String durability ;
1059- private final Component itemName ;
1060- private int lastSeconds = Integer .MIN_VALUE ;
1061- private Component lastFormatted ;
1037+ private final DroppedItemLabelFormatter .State formatState ;
1038+ private int appliedLabelId = Integer .MIN_VALUE ;
1039+ private Component appliedText ;
10621040
1063- private CachedItemContent (ItemStack stack , boolean blacklisted , String durability ,
1064- Component itemName ) {
1041+ private CachedItemContent (ItemStack stack , boolean blacklisted ,
1042+ DroppedItemLabelFormatter . State formatState ) {
10651043 this .stack = stack ;
10661044 this .blacklisted = blacklisted ;
1067- this .durability = durability ;
1068- this .itemName = itemName ;
1045+ this .formatState = formatState ;
10691046 }
10701047 }
10711048
0 commit comments