🆙 Update ItemsComponent & NPE Roomunit

This does not try to auto-repair the broken room layout. A room with bad
door coordinates is a database / model-authoring issue that needs to
be fixed at the source; this patch only stops the gameserver from
crashing on every entry attempt and leaving permanent ghost units
behind. The warning log line identifies the specific room id so the
operator can investigate which rooms are broken.
This commit is contained in:
duckietm
2026-04-08 15:47:53 +02:00
parent 821846a005
commit 559231e23e
4 changed files with 68 additions and 87 deletions
@@ -775,6 +775,15 @@ public class RoomManager {
habbo.getRoomUnit().setHeadRotation(RoomUserRotation.values()[room.getLayout().getDoorDirection()]);
}
if (habbo.getRoomUnit().getCurrentLocation() == null) {
LOGGER.warn("Failed to resolve a valid door tile for room {} ({}) while {} was entering; sending user back to hotel view",
room.getId(), room.getName(), habbo.getHabboInfo().getUsername());
habbo.getHabboInfo().setLoadingRoom(0);
habbo.getHabboInfo().setCurrentRoom(null);
habbo.getClient().sendResponse(new HotelViewComposer());
return;
}
habbo.getRoomUnit().setPathFinderRoom(room);
habbo.getRoomUnit().resetIdleTimer();
@@ -410,11 +410,11 @@ public class RoomUnit {
}
public short getX() {
return this.currentLocation.x;
return this.currentLocation == null ? 0 : this.currentLocation.x;
}
public short getY() {
return this.currentLocation.y;
return this.currentLocation == null ? 0 : this.currentLocation.y;
}
public double getZ() {
@@ -597,6 +597,7 @@ public class RoomUnit {
}
public boolean isAtGoal() {
if (this.currentLocation == null) return true;
return this.currentLocation.equals(this.goalLocation);
}
@@ -1,6 +1,7 @@
package com.eu.habbo.habbohotel.users.inventory;
import com.eu.habbo.Emulator;
import com.eu.habbo.database.SqlQueries;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboInventory;
@@ -9,7 +10,6 @@ import com.eu.habbo.plugin.events.inventory.InventoryItemAddedEvent;
import com.eu.habbo.plugin.events.inventory.InventoryItemRemovedEvent;
import com.eu.habbo.plugin.events.inventory.InventoryItemsAddedEvent;
import gnu.trove.TCollections;
import gnu.trove.iterator.TIntObjectIterator;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.THashMap;
import gnu.trove.map.hash.TIntObjectHashMap;
@@ -18,11 +18,9 @@ import gnu.trove.set.hash.THashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.NoSuchElementException;
import java.util.ArrayList;
import java.util.List;
public class ItemsComponent {
private static final Logger LOGGER = LoggerFactory.getLogger(ItemsComponent.class);
@@ -39,25 +37,23 @@ public class ItemsComponent {
public static THashMap<Integer, HabboItem> loadItems(Habbo habbo) {
THashMap<Integer, HabboItem> itemsList = new THashMap<>();
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT * FROM items WHERE room_id = ? AND user_id = ?")) {
statement.setInt(1, 0);
statement.setInt(2, habbo.getHabboInfo().getId());
try (ResultSet set = statement.executeQuery()) {
while (set.next()) {
try {
HabboItem item = Emulator.getGameEnvironment().getItemManager().loadHabboItem(set);
SqlQueries.forEach(
"SELECT * FROM items WHERE room_id = ? AND user_id = ?",
rs -> {
try {
HabboItem item = Emulator.getGameEnvironment().getItemManager().loadHabboItem(rs);
if (item != null) {
itemsList.put(set.getInt("id"), item);
itemsList.put(rs.getInt("id"), item);
} else {
LOGGER.error("Failed to load HabboItem: {}", set.getInt("id"));
LOGGER.error("Failed to load HabboItem: {}", rs.getInt("id"));
}
} catch (SQLException e) {
LOGGER.error("Caught SQL exception", e);
}
}
}
} catch (SQLException e) {
},
0, habbo.getHabboInfo().getId());
} catch (SqlQueries.DataAccessException e) {
LOGGER.error("Caught SQL exception", e);
}
@@ -151,70 +147,45 @@ public class ItemsComponent {
public void dispose() {
synchronized (this.items) {
TIntObjectIterator<HabboItem> items = this.items.iterator();
if (items == null) {
LOGGER.error("Items is NULL!");
return;
}
if (!this.items.isEmpty()) {
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) {
try (PreparedStatement updateStmt = connection.prepareStatement(
"UPDATE items SET user_id = ?, room_id = ?, wall_pos = ?, x = ?, y = ?, z = ?, rot = ?, extra_data = ?, limited_data = ? WHERE id = ?")) {
try (PreparedStatement deleteStmt = connection.prepareStatement(
"DELETE FROM items WHERE id = ?")) {
int updateCount = 0;
int deleteCount = 0;
for (int i = this.items.size(); i-- > 0; ) {
try {
items.advance();
} catch (NoSuchElementException e) {
break;
}
HabboItem item = items.value();
List<HabboItem> updates = new ArrayList<>();
List<HabboItem> deletes = new ArrayList<>();
for (HabboItem item : this.items.valueCollection()) {
if (item.needsDelete()) {
deleteStmt.setInt(1, item.getId());
deleteStmt.addBatch();
deleteCount++;
deletes.add(item);
item.needsUpdate(false);
item.needsDelete(false);
} else if (item.needsUpdate()) {
updateStmt.setInt(1, item.getUserId());
updateStmt.setInt(2, item.getRoomId());
updateStmt.setString(3, item.getWallPosition());
updateStmt.setInt(4, item.getX());
updateStmt.setInt(5, item.getY());
updateStmt.setDouble(6, item.getZ());
updateStmt.setInt(7, item.getRotation());
updateStmt.setString(8, item.getExtradata());
updateStmt.setString(9, item.getLimitedStack() + ":" + item.getLimitedSells());
updateStmt.setInt(10, item.getId());
updateStmt.addBatch();
updateCount++;
updates.add(item);
item.needsUpdate(false);
}
if (updateCount > 0 && updateCount % 100 == 0) {
updateStmt.executeBatch();
}
if (deleteCount > 0 && deleteCount % 100 == 0) {
deleteStmt.executeBatch();
}
}
if (deleteCount % 100 != 0) {
deleteStmt.executeBatch();
try {
if (!deletes.isEmpty()) {
SqlQueries.batchUpdate(
"DELETE FROM items WHERE id = ?",
deletes,
(ps, item) -> ps.setInt(1, item.getId()));
}
if (updateCount % 100 != 0) {
updateStmt.executeBatch();
if (!updates.isEmpty()) {
SqlQueries.batchUpdate(
"UPDATE items SET user_id = ?, room_id = ?, wall_pos = ?, x = ?, y = ?, z = ?, rot = ?, extra_data = ?, limited_data = ? WHERE id = ?",
updates,
(ps, item) -> {
ps.setInt(1, item.getUserId());
ps.setInt(2, item.getRoomId());
ps.setString(3, item.getWallPosition());
ps.setInt(4, item.getX());
ps.setInt(5, item.getY());
ps.setDouble(6, item.getZ());
ps.setInt(7, item.getRotation());
ps.setString(8, item.getExtradata());
ps.setString(9, item.getLimitedStack() + ":" + item.getLimitedSells());
ps.setInt(10, item.getId());
});
}
}
}
} catch (SQLException e) {
} catch (SqlQueries.DataAccessException e) {
LOGGER.error("Caught SQL exception during batch item save", e);
}
}