@@ -72,15 +72,23 @@ public void restartAllHoppers() {
7272 }
7373
7474 private void processChunkHoppers (Chunk chunk ) {
75+ if (chunk == null || !chunk .isLoaded ()) return ;
76+
7577 Location chunkLoc = new Location (chunk .getWorld (),
7678 chunk .getX () * 16 + 8 , 64 , chunk .getZ () * 16 + 8 );
7779
7880 Scheduler .runLocationTask (chunkLoc , () -> {
7981 try {
80- for (BlockState state : chunk .getTileEntities (block -> block .getType () == Material .HOPPER , false )) {
82+ Collection <BlockState > hopperStates = chunk .getTileEntities (block -> block .getType () == Material .HOPPER , false );
83+ if (hopperStates == null || hopperStates .isEmpty ()) return ;
84+
85+ for (BlockState state : hopperStates ) {
86+ if (state == null ) continue ;
87+
8188 Block hopperBlock = state .getBlock ();
82- Block aboveBlock = hopperBlock . getRelative ( BlockFace . UP ) ;
89+ if ( hopperBlock . getType () != Material . HOPPER ) continue ;
8390
91+ Block aboveBlock = hopperBlock .getRelative (BlockFace .UP );
8492 if (aboveBlock .getType () == Material .SPAWNER ) {
8593 startHopperTask (hopperBlock .getLocation (), aboveBlock .getLocation ());
8694 }
@@ -102,15 +110,42 @@ public void onChunkLoad(ChunkLoadEvent event) {
102110
103111 @ EventHandler
104112 public void onChunkUnload (ChunkUnloadEvent event ) {
113+ if (!plugin .getConfig ().getBoolean ("hopper.enabled" , false )) return ;
114+
105115 Chunk chunk = event .getChunk ();
106- for (BlockState state : chunk .getTileEntities (block -> block .getType () == Material .HOPPER , false )) {
107- stopHopperTask (state .getLocation ());
116+ if (chunk == null ) return ;
117+
118+ try {
119+ Collection <BlockState > hopperStates = chunk .getTileEntities (block -> block .getType () == Material .HOPPER , false );
120+ if (hopperStates == null || hopperStates .isEmpty ()) return ;
121+
122+ for (BlockState state : hopperStates ) {
123+ if (state == null ) continue ;
124+ stopHopperTask (state .getLocation ());
125+ }
126+ } catch (Exception e ) {
127+ plugin .getLogger ().log (Level .WARNING , "Error stopping hoppers in unloading chunk at " +
128+ chunk .getX () + "," + chunk .getZ (), e );
108129 }
109130 }
110131
111132 public void cleanup () {
112- activeHoppers .values ().forEach (Scheduler .Task ::cancel );
113- activeHoppers .clear ();
133+ try {
134+ List <Scheduler .Task > tasks = new ArrayList <>(activeHoppers .values ());
135+ activeHoppers .clear ();
136+
137+ for (Scheduler .Task task : tasks ) {
138+ if (task != null ) {
139+ try {
140+ task .cancel ();
141+ } catch (Exception e ) {
142+ plugin .getLogger ().log (Level .WARNING , "Error cancelling hopper task during cleanup" , e );
143+ }
144+ }
145+ }
146+ } catch (Exception e ) {
147+ plugin .getLogger ().log (Level .SEVERE , "Error during hopper handler cleanup" , e );
148+ }
114149 }
115150
116151 @ EventHandler
@@ -133,9 +168,10 @@ public void onHopperBreak(BlockBreakEvent event) {
133168
134169 public void startHopperTask (Location hopperLoc , Location spawnerLoc ) {
135170 if (!plugin .getConfig ().getBoolean ("hopper.enabled" , false )) return ;
136- if (activeHoppers .containsKey (hopperLoc )) return ;
171+ if (hopperLoc == null || spawnerLoc == null ) return ;
172+
173+ stopHopperTask (hopperLoc );
137174
138- // Create a runnable for the hopper task
139175 Runnable hopperRunnable = () -> {
140176 try {
141177 if (!isValidSetup (hopperLoc , spawnerLoc )) {
@@ -145,11 +181,9 @@ public void startHopperTask(Location hopperLoc, Location spawnerLoc) {
145181 transferItems (hopperLoc , spawnerLoc );
146182 } catch (Exception e ) {
147183 plugin .getLogger ().log (Level .WARNING , "Error in hopper task at " + hopperLoc , e );
148- // Don't stop the task on error, just log it
149184 }
150185 };
151186
152- // Use the location-based scheduler for better Folia compatibility
153187 try {
154188 Scheduler .Task task = Scheduler .runLocationTaskTimer (
155189 hopperLoc ,
@@ -158,7 +192,9 @@ public void startHopperTask(Location hopperLoc, Location spawnerLoc) {
158192 plugin .getTimeFromConfig ("hopper.check_delay" , "3s" )
159193 );
160194
161- activeHoppers .put (hopperLoc , task );
195+ if (task != null && !task .isCancelled ()) {
196+ activeHoppers .put (hopperLoc , task );
197+ }
162198 } catch (Exception e ) {
163199 plugin .getLogger ().log (Level .SEVERE , "Failed to start hopper task at " + hopperLoc , e );
164200 }
@@ -174,81 +210,108 @@ private boolean isValidSetup(Location hopperLoc, Location spawnerLoc) {
174210 }
175211
176212 public void stopHopperTask (Location hopperLoc ) {
213+ if (hopperLoc == null ) return ;
214+
177215 Scheduler .Task task = activeHoppers .remove (hopperLoc );
178216 if (task != null ) {
179- task .cancel ();
217+ try {
218+ task .cancel ();
219+ } catch (Exception e ) {
220+ plugin .getLogger ().log (Level .WARNING , "Error cancelling hopper task at " + hopperLoc , e );
221+ }
180222 }
181223 }
182224
183225 public void restartHopperForSpawner (Location spawnerLoc ) {
184226 if (!plugin .getConfig ().getBoolean ("hopper.enabled" , false )) return ;
185-
186- // Find hopper below the spawner
187- Block spawnerBlock = spawnerLoc .getBlock ();
188- if (spawnerBlock .getType () != Material .SPAWNER ) return ;
189-
190- Block hopperBlock = spawnerBlock .getRelative (BlockFace .DOWN );
191- if (hopperBlock .getType () != Material .HOPPER ) return ;
192-
193- Location hopperLoc = hopperBlock .getLocation ();
194-
195- // Stop existing hopper task if any
196- stopHopperTask (hopperLoc );
197-
198- // Start new hopper task
199- startHopperTask (hopperLoc , spawnerLoc );
227+ if (spawnerLoc == null ) return ;
228+
229+ Scheduler .runLocationTask (spawnerLoc , () -> {
230+ try {
231+ Block spawnerBlock = spawnerLoc .getBlock ();
232+ if (spawnerBlock .getType () != Material .SPAWNER ) return ;
233+
234+ Block hopperBlock = spawnerBlock .getRelative (BlockFace .DOWN );
235+ if (hopperBlock .getType () != Material .HOPPER ) return ;
236+
237+ Location hopperLoc = hopperBlock .getLocation ();
238+
239+ startHopperTask (hopperLoc , spawnerLoc );
240+ } catch (Exception e ) {
241+ plugin .getLogger ().log (Level .WARNING , "Error restarting hopper for spawner at " + spawnerLoc , e );
242+ }
243+ });
200244 }
201245
202246 private void transferItems (Location hopperLoc , Location spawnerLoc ) {
247+ if (hopperLoc == null || spawnerLoc == null ) return ;
248+
203249 SpawnerData spawner = spawnerManager .getSpawnerByLocation (spawnerLoc );
204250 if (spawner == null ) return ;
205251
206- // Use inventoryLock for hopper transfers
207252 ReentrantLock lock = spawner .getInventoryLock ();
208- if (!lock .tryLock ()) return ; // Skip this tick if we can't get the lock
253+ if (!lock .tryLock ()) return ;
209254
210255 try {
256+ Block hopperBlock = hopperLoc .getBlock ();
257+ if (hopperBlock .getType () != Material .HOPPER ) {
258+ stopHopperTask (hopperLoc );
259+ return ;
260+ }
261+
211262 VirtualInventory virtualInv = spawner .getVirtualInventory ();
212- Hopper hopper = (Hopper ) hopperLoc .getBlock ().getState (false );
263+ if (virtualInv == null ) return ;
264+
265+ Hopper hopper = (Hopper ) hopperBlock .getState (false );
266+ if (hopper == null ) return ;
267+
268+ Map <Integer , ItemStack > displayItems = virtualInv .getDisplayInventory ();
269+ if (displayItems == null || displayItems .isEmpty ()) return ;
213270
214271 int itemsPerTransfer = plugin .getConfig ().getInt ("hopper.stack_per_transfer" , 5 );
272+
273+ org .bukkit .inventory .Inventory hopperInv = hopper .getInventory ();
274+
275+ List <ItemStack > itemsToRemove = new ArrayList <>(itemsPerTransfer );
215276 int transferred = 0 ;
216277 boolean inventoryChanged = false ;
217278
218- Map <Integer , ItemStack > displayItems = virtualInv .getDisplayInventory ();
219- List <ItemStack > itemsToRemove = new ArrayList <>();
220-
221279 for (Map .Entry <Integer , ItemStack > entry : displayItems .entrySet ()) {
222280 if (transferred >= itemsPerTransfer ) break ;
223281
224282 ItemStack item = entry .getValue ();
225283 if (item == null || item .getType () == Material .AIR ) continue ;
226284
227- ItemStack [] hopperContents = hopper . getInventory () .getContents ();
228- for ( int i = 0 ; i < hopperContents . length ; i ++) {
229- if ( transferred >= itemsPerTransfer ) break ;
230-
285+ ItemStack [] hopperContents = hopperInv .getContents ();
286+
287+ boolean itemTransferred = false ;
288+ for ( int i = 0 ; i < hopperContents . length && ! itemTransferred ; i ++) {
231289 ItemStack hopperItem = hopperContents [i ];
290+
232291 if (hopperItem == null || hopperItem .getType () == Material .AIR ) {
233- hopper . getInventory () .setItem (i , item .clone ());
292+ hopperInv .setItem (i , item .clone ());
234293 itemsToRemove .add (item );
235294 transferred ++;
236295 inventoryChanged = true ;
237- break ;
238- } else if (hopperItem .isSimilar (item ) &&
239- hopperItem .getAmount () < hopperItem .getMaxStackSize ()) {
240- int space = hopperItem .getMaxStackSize () - hopperItem .getAmount ();
241- int toTransfer = Math .min (space , item .getAmount ());
242-
243- hopperItem .setAmount (hopperItem .getAmount () + toTransfer );
244-
245- ItemStack toRemove = item .clone ();
246- toRemove .setAmount (toTransfer );
247- itemsToRemove .add (toRemove );
248-
249- transferred ++;
250- inventoryChanged = true ;
251- break ;
296+ itemTransferred = true ;
297+ } else if (hopperItem .isSimilar (item )) {
298+ int currentAmount = hopperItem .getAmount ();
299+ int maxStackSize = hopperItem .getMaxStackSize ();
300+
301+ if (currentAmount < maxStackSize ) {
302+ int space = maxStackSize - currentAmount ;
303+ int toTransfer = Math .min (space , item .getAmount ());
304+
305+ hopperItem .setAmount (currentAmount + toTransfer );
306+
307+ ItemStack toRemove = item .clone ();
308+ toRemove .setAmount (toTransfer );
309+ itemsToRemove .add (toRemove );
310+
311+ transferred ++;
312+ inventoryChanged = true ;
313+ itemTransferred = true ;
314+ }
252315 }
253316 }
254317 }
@@ -261,7 +324,7 @@ private void transferItems(Location hopperLoc, Location spawnerLoc) {
261324 updateOpenGuis (spawner );
262325 }
263326 } catch (Exception e ) {
264- plugin .getLogger ().log (Level .WARNING , "Error transferring items from spawner to hopper" , e );
327+ plugin .getLogger ().log (Level .WARNING , "Error transferring items from spawner to hopper at " + hopperLoc , e );
265328 } finally {
266329 lock .unlock ();
267330 }
0 commit comments