diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/RedeemClothingEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/RedeemClothingEvent.java index 496d521d..2ab95a0d 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/RedeemClothingEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/RedeemClothingEvent.java @@ -36,6 +36,10 @@ public class RedeemClothingEvent extends MessageHandler { if (clothing != null) { if (!this.client.getHabbo().getInventory().getWardrobeComponent().getClothing().contains(clothing.id)) { + if (!this.grantClothing(clothing.id)) { + return; + } + item.setRoomId(0); RoomTile tile = this.client.getHabbo().getHabboInfo().getCurrentRoom().getLayout().getTile(item.getX(), item.getY()); this.client.getHabbo().getHabboInfo().getCurrentRoom().removeHabboItem(item); @@ -44,14 +48,6 @@ public class RedeemClothingEvent extends MessageHandler { this.client.getHabbo().getHabboInfo().getCurrentRoom().sendComposer(new RemoveFloorItemComposer(item, true).compose()); Emulator.getThreading().run(new QueryDeleteHabboItem(item.getId())); - try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO users_clothing (user_id, clothing_id) VALUES (?, ?)")) { - statement.setInt(1, this.client.getHabbo().getHabboInfo().getId()); - statement.setInt(2, clothing.id); - statement.execute(); - } catch (SQLException e) { - LOGGER.error("Caught SQL exception", e); - } - this.client.getHabbo().getInventory().getWardrobeComponent().getClothing().add(clothing.id); this.client.getHabbo().getInventory().getWardrobeComponent().getClothingSets().addAll(clothing.setId); this.client.sendResponse(new UserClothesComposer(this.client.getHabbo())); @@ -67,4 +63,15 @@ public class RedeemClothingEvent extends MessageHandler { } } } + + private boolean grantClothing(int clothingId) { + try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO users_clothing (user_id, clothing_id) VALUES (?, ?)")) { + statement.setInt(1, this.client.getHabbo().getHabboInfo().getId()); + statement.setInt(2, clothingId); + return statement.executeUpdate() > 0; + } catch (SQLException e) { + LOGGER.error("Caught SQL exception", e); + return false; + } + } } diff --git a/Emulator/src/test/java/com/eu/habbo/messages/incoming/rooms/items/RedeemClothingContractTest.java b/Emulator/src/test/java/com/eu/habbo/messages/incoming/rooms/items/RedeemClothingContractTest.java new file mode 100644 index 00000000..fcbf35c7 --- /dev/null +++ b/Emulator/src/test/java/com/eu/habbo/messages/incoming/rooms/items/RedeemClothingContractTest.java @@ -0,0 +1,29 @@ +package com.eu.habbo.messages.incoming.rooms.items; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.jupiter.api.Test; + +class RedeemClothingContractTest { + private static String source() throws Exception { + return Files.readString(Path.of("src/main/java/com/eu/habbo/messages/incoming/rooms/items/RedeemClothingEvent.java")); + } + + @Test + void clothingIsGrantedBeforeVoucherFurnitureIsConsumed() throws Exception { + String source = source(); + + int grantCall = source.indexOf("grantClothing("); + int roomRemoval = source.indexOf("removeHabboItem(item)"); + int deleteItem = source.indexOf("new QueryDeleteHabboItem(item.getId())"); + + assertTrue(source.contains("private boolean grantClothing(int clothingId)"), + "clothing DB insert should report whether the grant succeeded"); + assertTrue(grantCall > -1, "redeem path should call grantClothing before consuming the item"); + assertTrue(grantCall < roomRemoval, "room item must not be removed before the clothing grant succeeds"); + assertTrue(grantCall < deleteItem, "voucher furniture must not be deleted before the clothing grant succeeds"); + } +}