1212import net .minecraft .client .gui .components .Button ;
1313import net .minecraft .client .gui .screens .inventory .AbstractContainerScreen ;
1414import net .minecraft .client .gui .screens .inventory .ShulkerBoxScreen ;
15+ import net .minecraft .core .BlockPos ;
1516import net .minecraft .network .chat .Component ;
1617import net .minecraft .world .entity .player .Inventory ;
1718import net .minecraft .world .inventory .ShulkerBoxMenu ;
19+ import net .minecraft .world .level .block .ShulkerBoxBlock ;
20+ import net .minecraft .world .level .block .entity .BlockEntity ;
21+ import net .minecraft .world .level .block .state .BlockState ;
22+ import net .minecraft .world .phys .BlockHitResult ;
23+ import net .minecraft .world .phys .HitResult ;
1824import net .wurstclient .WurstClient ;
25+ import net .wurstclient .chestsearch .ChestConfig ;
26+ import net .wurstclient .chestsearch .ChestRecorder ;
27+ import net .wurstclient .clickgui .screens .ChestSearchScreen ;
1928import net .wurstclient .hacks .AutoStealHack ;
29+ import net .wurstclient .hacks .ChestSearchHack ;
2030import net .wurstclient .hacks .QuickShulkerHack ;
2131
2232@ Mixin (ShulkerBoxScreen .class )
@@ -29,6 +39,18 @@ public abstract class ShulkerBoxScreenMixin
2939 @ Unique
3040 private final QuickShulkerHack quickShulker =
3141 WurstClient .INSTANCE .getHax ().quickShulkerHack ;
42+ @ Unique
43+ private ChestRecorder chestRecorder ;
44+ @ Unique
45+ private BlockPos shulkerPos ;
46+ @ Unique
47+ private BlockPos shulkerClickedPos ;
48+ @ Unique
49+ private String shulkerServerIp ;
50+ @ Unique
51+ private String shulkerDimension ;
52+ @ Unique
53+ private boolean shulkerSnapshotFinalized = false ;
3254
3355 private ShulkerBoxScreenMixin (WurstClient wurst , ShulkerBoxMenu handler ,
3456 Inventory inventory , Component title )
@@ -95,5 +117,190 @@ public void init()
95117 quickButton .active = !quickShulker .isBusy ();
96118 addRenderableWidget (quickButton );
97119 }
120+
121+ try
122+ {
123+ ChestSearchHack chestSearchHack =
124+ WurstClient .INSTANCE .getHax ().chestSearchHack ;
125+ if (chestSearchHack != null && chestSearchHack .isOffMode ())
126+ return ;
127+
128+ ChestConfig cfg = new ChestConfig ();
129+ if (!cfg .enabled )
130+ return ;
131+
132+ this .chestRecorder =
133+ new ChestRecorder (new java .io .File (cfg .dbPath ), cfg );
134+
135+ String serverIp = null ;
136+ try
137+ {
138+ if (WurstClient .MC != null
139+ && WurstClient .MC .getCurrentServer () != null )
140+ serverIp = WurstClient .MC .getCurrentServer ().ip ;
141+ }catch (Throwable ignored )
142+ {}
143+ String dimension = null ;
144+ try
145+ {
146+ if (WurstClient .MC != null && WurstClient .MC .level != null )
147+ dimension = WurstClient .MC .level .dimension ().identifier ()
148+ .toString ();
149+ }catch (Throwable ignored )
150+ {}
151+ shulkerServerIp = serverIp ;
152+ shulkerDimension = dimension ;
153+
154+ shulkerPos = wurst$resolveShulkerPos ();
155+ if (shulkerPos == null )
156+ return ;
157+
158+ try
159+ {
160+ if (shulkerClickedPos != null )
161+ {
162+ boolean cleared = ChestSearchScreen
163+ .clearDecorations (dimension , shulkerClickedPos );
164+ if (!cleared )
165+ ChestSearchScreen .clearDecorations (dimension ,
166+ shulkerPos );
167+ }else
168+ {
169+ ChestSearchScreen .clearDecorations (dimension , shulkerPos );
170+ }
171+ }catch (Throwable ignored )
172+ {}
173+
174+ ChestRecorder .Bounds bounds =
175+ ChestRecorder .Bounds .of (shulkerPos , shulkerPos , null );
176+ chestRecorder .onChestOpened (serverIp , dimension , shulkerPos .getX (),
177+ shulkerPos .getY (), shulkerPos .getZ (), this .menu , 27 ,
178+ java .util .Collections .emptyList (), bounds );
179+ }catch (Throwable ignored )
180+ {}
181+ }
182+
183+ @ Override
184+ public void removed ()
185+ {
186+ wurst$finalizeShulkerSnapshot ();
187+ super .removed ();
188+ }
189+
190+ @ Unique
191+ private void wurst$finalizeShulkerSnapshot ()
192+ {
193+ if (shulkerSnapshotFinalized )
194+ return ;
195+ shulkerSnapshotFinalized = true ;
196+ if (chestRecorder == null || shulkerPos == null || this .menu == null )
197+ return ;
198+
199+ try
200+ {
201+ int total = this .menu .slots .size ();
202+ int window = Math .min (27 , total );
203+ java .util .List <Integer > slotOrder =
204+ new java .util .ArrayList <>(window );
205+ java .util .List <net .minecraft .world .item .ItemStack > region =
206+ new java .util .ArrayList <>(window );
207+ boolean any = false ;
208+ for (int i = 0 ; i < window ; i ++)
209+ {
210+ slotOrder .add (i );
211+ var st = this .menu .slots .get (i ).getItem ();
212+ region .add (st );
213+ if (st != null && !st .isEmpty ())
214+ any = true ;
215+ }
216+ if (any && !region .isEmpty ())
217+ {
218+ ChestRecorder .Bounds bounds =
219+ ChestRecorder .Bounds .of (shulkerPos , shulkerPos , null );
220+ chestRecorder .recordFromStacksWithSlotOrder (shulkerServerIp ,
221+ shulkerDimension , shulkerPos .getX (), shulkerPos .getY (),
222+ shulkerPos .getZ (), region , slotOrder , bounds );
223+ }
224+ }catch (Throwable ignored )
225+ {}
226+ }
227+
228+ @ Unique
229+ private BlockPos wurst$resolveShulkerPos ()
230+ {
231+ BlockPos resolvedPos = null ;
232+ try
233+ {
234+ java .lang .reflect .Method gb =
235+ this .menu .getClass ().getMethod ("getBlockEntity" );
236+ Object be = gb .invoke (this .menu );
237+ if (be instanceof BlockEntity )
238+ resolvedPos = ((BlockEntity )be ).getBlockPos ();
239+ }catch (Throwable ignored )
240+ {}
241+
242+ if (resolvedPos == null )
243+ {
244+ try
245+ {
246+ Object ctxObj = null ;
247+ try
248+ {
249+ java .lang .reflect .Method m =
250+ this .menu .getClass ().getMethod ("getContext" );
251+ ctxObj = m .invoke (this .menu );
252+ }catch (Throwable ignored )
253+ {}
254+ if (ctxObj instanceof net .minecraft .world .inventory .ContainerLevelAccess ctx )
255+ {
256+ BlockPos [] holder = new BlockPos [1 ];
257+ ctx .execute ((world , pos ) -> holder [0 ] = pos );
258+ resolvedPos = holder [0 ];
259+ }
260+ }catch (Throwable ignored )
261+ {}
262+ }
263+
264+ if (resolvedPos == null )
265+ {
266+ try
267+ {
268+ HitResult hr = WurstClient .MC .hitResult ;
269+ if (hr instanceof BlockHitResult bhr )
270+ {
271+ shulkerClickedPos = bhr .getBlockPos ();
272+ resolvedPos = shulkerClickedPos ;
273+ }
274+ }catch (Throwable ignored )
275+ {}
276+ }
277+
278+ if (resolvedPos == null )
279+ return null ;
280+
281+ BlockPos normalized = wurst$normalizeShulkerPos (resolvedPos );
282+ if (shulkerClickedPos == null && resolvedPos != null )
283+ shulkerClickedPos = resolvedPos ;
284+ return normalized ;
285+ }
286+
287+ @ Unique
288+ private BlockPos wurst$normalizeShulkerPos (BlockPos pos )
289+ {
290+ if (pos == null || WurstClient .MC == null
291+ || WurstClient .MC .level == null )
292+ return pos ;
293+
294+ BlockPos current = pos ;
295+ BlockState state = WurstClient .MC .level .getBlockState (current );
296+ if (!(state .getBlock () instanceof ShulkerBoxBlock ))
297+ {
298+ BlockPos below = current .below ();
299+ BlockState belowState = WurstClient .MC .level .getBlockState (below );
300+ if (belowState .getBlock () instanceof ShulkerBoxBlock )
301+ current = below ;
302+ }
303+
304+ return current ;
98305 }
99306}
0 commit comments