11package org .zeroBzeroT .antiillegals .helpers ;
22
3+ import de .tr7zw .changeme .nbtapi .NBT ;
4+ import de .tr7zw .changeme .nbtapi .iface .ReadWriteNBT ;
5+ import de .tr7zw .changeme .nbtapi .iface .ReadWriteNBTCompoundList ;
36import org .bukkit .Bukkit ;
47import org .bukkit .Location ;
58import org .bukkit .Material ;
1013import org .jetbrains .annotations .Nullable ;
1114import org .zeroBzeroT .antiillegals .AntiIllegals ;
1215
13- import java .util .Arrays ;
14- import java .util .Collection ;
15- import java .util .Objects ;
16+ import java .util .* ;
17+ import java .util .concurrent . atomic . AtomicBoolean ;
18+ import java .util .concurrent . atomic . AtomicInteger ;
1619import java .util .stream .Stream ;
1720
1821public class BookHelper {
@@ -21,32 +24,84 @@ private BookHelper() {
2124
2225 }
2326
24- public static int maxBookShulkers ( ) {
25- return AntiIllegals .config ().getInt ( "maxBooksShulkersInInventory" );
27+ private static boolean hasConfigValue ( @ NotNull final String path ) {
28+ return AntiIllegals .config ().isSet ( path );
2629 }
2730
28- public static int maxBookItemsInShulker () {
29- return AntiIllegals .config ().getInt ("maxBooksInShulker" );
31+ private static boolean getBoolean (@ NotNull final String newPath , @ Nullable final String legacyPath ,
32+ final boolean defaultValue ) {
33+ if (hasConfigValue (newPath ))
34+ return AntiIllegals .config ().getBoolean (newPath );
35+ if (legacyPath != null && hasConfigValue (legacyPath ))
36+ return AntiIllegals .config ().getBoolean (legacyPath );
37+ return defaultValue ;
38+ }
39+
40+ private static int getInt (@ NotNull final String newPath , @ Nullable final String legacyPath ,
41+ final int defaultValue ) {
42+ if (hasConfigValue (newPath ))
43+ return AntiIllegals .config ().getInt (newPath );
44+ if (legacyPath != null && hasConfigValue (legacyPath ))
45+ return AntiIllegals .config ().getInt (legacyPath );
46+ return defaultValue ;
47+ }
48+
49+ public static boolean shouldCheckLecternBooks () {
50+ return getBoolean ("books.lecternCheck" , null , true );
3051 }
3152
3253 public static boolean shouldCleanBookShulkers () {
33- return AntiIllegals .config ().getBoolean ("shulkerBoxes" );
54+ return getBoolean ("books.shulkerBoxes.enabled" , "shulkerBoxes" , true );
55+ }
56+
57+ public static int maxBookItemsInShulker () {
58+ return getInt ("books.shulkerBoxes.maxBooksInside" , "maxBooksInShulker" , 10 );
59+ }
60+
61+ public static int maxBookShulkers () {
62+ return getInt ("books.shulkerBoxes.maxContainersPerInventory" , "maxBooksShulkersInInventory" , 3 );
63+ }
64+
65+ public static boolean shouldCleanBundleBooks () {
66+ return getBoolean ("books.bundles.enabled" , null , true );
3467 }
3568
36- public static void dropBookShulkerItem (@ NotNull final Location location , @ NotNull final ItemStack itemStack ) {
69+ public static int maxBooksInBundle () {
70+ return getInt ("books.bundles.maxBooksInside" , null , 10 );
71+ }
72+
73+ public static int maxBookBundlesInInventory () {
74+ return getInt ("books.bundles.maxContainersPerInventory" , null , 1 );
75+ }
76+
77+ public static void dropBookContainerItem (@ NotNull final Location location , @ NotNull final ItemStack itemStack ) {
3778 location .getWorld ().dropItem (location , itemStack ).setPickupDelay (100 );
3879 }
3980
4081 public static int cleanBookItems (@ NotNull final Inventory inventory , @ Nullable final Location location ,
41- @ NotNull final Collection <ItemStack > shulkerWithBooksItemStack ) {
82+ @ NotNull final Collection <ItemStack > shulkerWithBooksItemStack ) {
83+ if (!shouldCleanBookShulkers ())
84+ return 0 ;
85+
4286 return cleanOversizedItems (inventory , location , shulkerWithBooksItemStack , maxBookItemsInShulker ());
4387 }
4488
4589 public static int cleanBookShulkers (@ NotNull final Inventory inventory , @ Nullable final Location location ,
46- @ NotNull final Collection <ItemStack > shulkerWithBooksItemStack ) {
90+ @ NotNull final Collection <ItemStack > shulkerWithBooksItemStack ) {
91+ if (!shouldCleanBookShulkers ())
92+ return 0 ;
93+
4794 return cleanOversizedItems (inventory , location , shulkerWithBooksItemStack , maxBookShulkers ());
4895 }
4996
97+ public static int cleanBookBundles (@ NotNull final Inventory inventory , @ Nullable final Location location ,
98+ @ NotNull final Collection <ItemStack > bundleItemStacks ) {
99+ if (!shouldCleanBundleBooks ())
100+ return 0 ;
101+
102+ return cleanOversizedItems (inventory , location , bundleItemStacks , maxBookBundlesInInventory ());
103+ }
104+
50105 public static boolean isBookItem (@ NotNull final ItemStack itemStack ) {
51106 return isBookItem (itemStack .getType ());
52107 }
@@ -85,12 +140,14 @@ public static boolean containsBooks(@NotNull final Inventory inventory) {
85140 * @return whether it contains books
86141 */
87142 public static boolean containsBooks (@ Nullable final ItemStack itemStack ) {
88- if (itemStack == null ) return false ;
143+ if (itemStack == null )
144+ return false ;
89145 return InventoryHolderHelper .mapInventory (itemStack , BookHelper ::containsBooks ).orElse (false );
90146 }
91147
92148 public static int cleanBookShulkers (@ NotNull final Inventory inventory , @ Nullable final Location location ) {
93- if (!shouldCleanBookShulkers ()) return 0 ;
149+ if (!shouldCleanBookShulkers ())
150+ return 0 ;
94151
95152 final Collection <ItemStack > shulkersWithBooks = Arrays .stream (inventory .getContents ())
96153 .filter (BookHelper ::containsBooks )
@@ -99,32 +156,134 @@ public static int cleanBookShulkers(@NotNull final Inventory inventory, @Nullabl
99156 return BookHelper .cleanBookShulkers (inventory , location , shulkersWithBooks );
100157 }
101158
159+ public static boolean bundleContainsBooks (@ NotNull final ItemStack itemStack ) {
160+ if (!shouldCleanBundleBooks () || itemStack .getType () != Material .BUNDLE )
161+ return false ;
162+
163+ final AtomicBoolean containsBooks = new AtomicBoolean (false );
164+
165+ NBT .modify (itemStack , nbtItem -> {
166+ final ReadWriteNBTCompoundList items = nbtItem .getCompoundList ("Items" );
167+ if (items == null )
168+ return ;
169+
170+ for (final ReadWriteNBT entry : items ) {
171+ if (entry == null )
172+ continue ;
173+
174+ final ItemStack stored = NBT .itemStackFromNBT (entry );
175+ if (stored == null )
176+ continue ;
177+
178+ if (!isBookItem (stored ))
179+ continue ;
180+
181+ containsBooks .set (true );
182+ break ;
183+ }
184+ });
185+
186+ return containsBooks .get ();
187+ }
188+
189+ public static int trimBundleBooks (@ NotNull final Collection <ItemStack > bundleItemStacks ,
190+ @ Nullable final Location location ) {
191+ if (!shouldCleanBundleBooks () || location == null || bundleItemStacks .isEmpty ())
192+ return 0 ;
193+
194+ final int maxBooks = maxBooksInBundle ();
195+ if (maxBooks < 0 )
196+ return 0 ;
197+
198+ int removed = 0 ;
199+ for (final ItemStack bundle : bundleItemStacks ) {
200+ removed += trimBooksFromBundle (bundle , location , maxBooks );
201+ }
202+ return removed ;
203+ }
204+
205+ private static int trimBooksFromBundle (@ NotNull final ItemStack bundle , @ NotNull final Location location ,
206+ final int maxBooks ) {
207+ final AtomicInteger removed = new AtomicInteger (0 );
208+
209+ NBT .modify (bundle , nbtItem -> {
210+ final ReadWriteNBTCompoundList items = nbtItem .getCompoundList ("Items" );
211+ if (items == null || items .isEmpty ())
212+ return ;
213+
214+ int seenBooks = 0 ;
215+ final List <Integer > removalIndexes = new ArrayList <>();
216+ final List <ItemStack > droppedBooks = new ArrayList <>();
217+
218+ for (int i = 0 ; i < items .size (); i ++) {
219+ final ReadWriteNBT entry = items .get (i );
220+ if (entry == null )
221+ continue ;
222+
223+ final ItemStack stored = NBT .itemStackFromNBT (entry );
224+ if (stored == null || !isBookItem (stored ))
225+ continue ;
226+
227+ if (seenBooks < maxBooks ) {
228+ seenBooks ++;
229+ continue ;
230+ }
231+
232+ removalIndexes .add (i );
233+ droppedBooks .add (stored );
234+ }
235+
236+ if (removalIndexes .isEmpty ())
237+ return ;
238+
239+ removalIndexes .sort (Integer ::compareTo );
240+ Collections .reverse (removalIndexes );
241+ removalIndexes .forEach (items ::remove );
242+
243+ removed .addAndGet (droppedBooks .size ());
244+ if (location .getWorld () != null )
245+ droppedBooks .forEach (stack -> dropBookContainerItem (location , stack ));
246+ });
247+
248+ return removed .get ();
249+ }
250+
102251 public static int cleanOversizedItems (@ NotNull final Inventory inventory , @ Nullable final Location location ,
103- @ NotNull final Collection <ItemStack > shulkerWithBooksItemStack ,
104- final int maxItems ) {
105- if (location == null || maxItems < 0 || shulkerWithBooksItemStack .size () <= maxItems ) return 0 ;
252+ @ NotNull final Collection <ItemStack > shulkerWithBooksItemStack ,
253+ final int maxItems ) {
254+ if (location == null || maxItems < 0 || shulkerWithBooksItemStack .size () <= maxItems )
255+ return 0 ;
106256
107257 int counter = 0 ;
108258
109259 for (final ItemStack shulkerItemStack : shulkerWithBooksItemStack ) {
110260 inventory .remove (shulkerItemStack );
111- Bukkit .getScheduler ().runTask (AntiIllegals .INSTANCE , () -> dropBookShulkerItem (location , shulkerItemStack ));
261+ Bukkit .getScheduler ().runTask (AntiIllegals .INSTANCE ,
262+ () -> dropBookContainerItem (location , shulkerItemStack ));
112263 counter ++;
113264 }
114265 return counter ;
115266 }
116267
117268 public static void checkEnderChest (@ NotNull final InventoryOpenEvent inventoryOpenEvent ,
118- @ Nullable final Location location ) {
269+ @ Nullable final Location location ) {
119270 final Inventory inventory = inventoryOpenEvent .getInventory ();
120271 final ItemStack [] inventoryContents = inventory .getContents ();
121272
122273 for (final ItemStack itemStack : inventoryContents ) {
123- if (itemStack == null ) continue ;
274+ if (itemStack == null )
275+ continue ;
124276
125- InventoryHolderHelper .iterateInventory (itemStack , inv ->
126- BookHelper .cleanBookItems (inv , location , BookHelper .filterBooks (inv ).toList ())
127- );
277+ InventoryHolderHelper .iterateInventory (itemStack ,
278+ inv -> BookHelper .cleanBookItems (inv , location , BookHelper .filterBooks (inv ).toList ()));
128279 }
280+
281+ final List <ItemStack > bundlesWithBooks = Arrays .stream (inventoryContents )
282+ .filter (Objects ::nonNull )
283+ .filter (BookHelper ::bundleContainsBooks )
284+ .toList ();
285+
286+ BookHelper .cleanBookBundles (inventory , location , bundlesWithBooks );
287+ BookHelper .trimBundleBooks (bundlesWithBooks , location );
129288 }
130289}
0 commit comments