@@ -19,6 +19,12 @@ public class ChestCleaner
1919{
2020 private final ChestManager manager ;
2121 private int tickCounter = 0 ;
22+ // ticks since we last observed a world/server change; used for grace
23+ // period after joining/reloading
24+ private int ticksSinceWorldObserved = 0 ;
25+ private String lastServer = null ;
26+ private String lastDimension = null ;
27+ // defaults (will be read from config at runtime)
2228
2329 public ChestCleaner ()
2430 {
@@ -39,6 +45,37 @@ public void register()
3945 MinecraftClient mc = WurstClient .MC ;
4046 if (mc == null || mc .world == null )
4147 return ;
48+
49+ // update world/server observation ticks and detect join/world
50+ // changes
51+ String curServer = null ;
52+ try
53+ {
54+ if (mc .getCurrentServerEntry () != null )
55+ curServer = mc .getCurrentServerEntry ().address ;
56+ }catch (Throwable ignored )
57+ {}
58+ String curDimension = null ;
59+ try
60+ {
61+ curDimension =
62+ mc .world .getRegistryKey ().getValue ().toString ();
63+ }catch (Throwable ignored )
64+ {}
65+ // reset grace timer on server/dimension change
66+ if (lastServer == null || !lastServer .equals (curServer )
67+ || lastDimension == null
68+ || !lastDimension .equals (curDimension ))
69+ {
70+ ticksSinceWorldObserved = 0 ;
71+ lastServer = curServer ;
72+ lastDimension = curDimension ;
73+ }else
74+ {
75+ // increase observed ticks
76+ ticksSinceWorldObserved += 100 ; // we check every 100 ticks
77+ // interval
78+ }
4279
4380 String serverIp = null ;
4481 try
@@ -66,44 +103,131 @@ public void register()
66103 continue ;
67104 try
68105 {
69- BlockPos pos = e .getMinPos ();
70- boolean chunkLoaded = false ;
71- try
106+ // read configured values
107+ int configuredGrace = manager .getConfig () == null ? 200
108+ : manager .getConfig ().graceTicks ;
109+ int configuredRadius = manager .getConfig () == null ? 64
110+ : manager .getConfig ().scanRadius ;
111+ // Only check entries when player is within scan radius
112+ // or when within the grace period after joining.
113+ if (ticksSinceWorldObserved < configuredGrace )
72114 {
73- @ SuppressWarnings ("deprecation" )
74- boolean tmp = mc .world .isChunkLoaded (pos );
75- chunkLoaded = tmp ;
76- }catch (Throwable ignored )
115+ // during grace period avoid deletions
116+ continue ;
117+ }
118+
119+ // Determine canonical bounds for the recorded chest
120+ int minX = Math .min (e .x , e .maxX );
121+ int minY = Math .min (e .y , e .maxY );
122+ int minZ = Math .min (e .z , e .maxZ );
123+ int maxX = Math .max (e .x , e .maxX );
124+ int maxY = Math .max (e .y , e .maxY );
125+ int maxZ = Math .max (e .z , e .maxZ );
126+
127+ // compute distance from player to nearest point in
128+ // bounds
129+ BlockPos playerPos = mc .player .getBlockPos ();
130+ int px = playerPos .getX ();
131+ int py = playerPos .getY ();
132+ int pz = playerPos .getZ ();
133+ int closestX = Math .max (minX , Math .min (px , maxX ));
134+ int closestY = Math .max (minY , Math .min (py , maxY ));
135+ int closestZ = Math .max (minZ , Math .min (pz , maxZ ));
136+ long dx = px - closestX ;
137+ long dy = py - closestY ;
138+ long dz = pz - closestZ ;
139+ long distSq = dx * dx + dy * dy + dz * dz ;
140+ if (distSq > (long )configuredRadius * configuredRadius )
141+ {
142+ // player too far to reliably observe this chest
143+ continue ;
144+ }
145+
146+ boolean anyChunkNotLoaded = false ;
147+ boolean anyContainerPresent = false ;
148+
149+ // Iterate all block positions inside the recorded
150+ // bounds.
151+ // If any overlapping chunk is not loaded, skip
152+ // deletion.
153+ // If any block in the bounds is a container and has a
154+ // block entity,
155+ // skip deletion. Only delete when all chunks are loaded
156+ // and
157+ // NO matching container+block-entity exists in the
158+ // bounds.
159+ outer : for (int bx = minX ; bx <= maxX ; bx ++)
77160 {
78- try
161+ for ( int by = minY ; by <= maxY ; by ++)
79162 {
80- Object cm = mc .world .getChunkManager ();
81- java .lang .reflect .Method m =
82- cm .getClass ().getMethod ("isChunkLoaded" ,
83- int .class , int .class );
84- chunkLoaded = Boolean .TRUE .equals (m .invoke (cm ,
85- pos .getX () >> 4 , pos .getZ () >> 4 ));
86- }catch (Throwable ignored2 )
87- {}
163+ for (int bz = minZ ; bz <= maxZ ; bz ++)
164+ {
165+ BlockPos pos = new BlockPos (bx , by , bz );
166+ boolean chunkLoaded = false ;
167+ try
168+ {
169+ @ SuppressWarnings ("deprecation" )
170+ boolean tmp =
171+ mc .world .isChunkLoaded (pos );
172+ chunkLoaded = tmp ;
173+ }catch (Throwable ignored )
174+ {
175+ try
176+ {
177+ Object cm =
178+ mc .world .getChunkManager ();
179+ java .lang .reflect .Method m =
180+ cm .getClass ().getMethod (
181+ "isChunkLoaded" , int .class ,
182+ int .class );
183+ chunkLoaded = Boolean .TRUE .equals (
184+ m .invoke (cm , pos .getX () >> 4 ,
185+ pos .getZ () >> 4 ));
186+ }catch (Throwable ignored2 )
187+ {}
188+ }
189+ if (!chunkLoaded )
190+ {
191+ anyChunkNotLoaded = true ;
192+ break outer ; // abort: wait until all
193+ // chunks loaded
194+ }
195+
196+ var state = mc .world .getBlockState (pos );
197+ boolean containerBlock =
198+ state != null && (state
199+ .getBlock () instanceof net .minecraft .block .ChestBlock
200+ || state
201+ .getBlock () instanceof net .minecraft .block .BarrelBlock
202+ || state
203+ .getBlock () instanceof net .minecraft .block .ShulkerBoxBlock
204+ || state
205+ .getBlock () instanceof net .minecraft .block .DecoratedPotBlock );
206+ boolean hasBe =
207+ mc .world .getBlockEntity (pos ) != null ;
208+ if (containerBlock && hasBe )
209+ {
210+ anyContainerPresent = true ;
211+ break outer ; // container still present,
212+ // do not delete
213+ }
214+ }
215+ }
88216 }
89- if (!chunkLoaded )
90- continue ; // don't delete when chunk is unloaded
91- var state = mc .world .getBlockState (pos );
92- boolean containerBlock = state != null && (state
93- .getBlock () instanceof net .minecraft .block .ChestBlock
94- || state
95- .getBlock () instanceof net .minecraft .block .BarrelBlock
96- || state
97- .getBlock () instanceof net .minecraft .block .ShulkerBoxBlock
98- || state
99- .getBlock () instanceof net .minecraft .block .DecoratedPotBlock );
100- boolean hasBe = mc .world .getBlockEntity (pos ) != null ;
101- if (!hasBe || !containerBlock )
217+
218+ if (anyChunkNotLoaded )
219+ continue ; // wait until all relevant chunks are
220+ // loaded
221+
222+ if (!anyContainerPresent )
102223 {
224+ // No container found in the recorded bounds and all
225+ // chunks covering the bounds are loaded -> safe to
226+ // delete
103227 ChestSearchScreen .clearDecorations (e .dimension ,
104- pos );
105- manager .removeChest (e .serverIp , e .dimension ,
106- pos . getX (), pos . getY (), pos . getZ () );
228+ e . getMinPos () );
229+ manager .removeChest (e .serverIp , e .dimension , minX ,
230+ minY , minZ );
107231 }
108232 }catch (Throwable ignored )
109233 {}
0 commit comments