You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-19 15:06:19 +00:00
Merge pull request #180 from simoleo89/fix/items-ownership-and-charges
fix(items): harden ownership and redeem lifecycle
This commit is contained in:
@@ -179,9 +179,13 @@ public class BotManager {
|
||||
}
|
||||
|
||||
public void pickUpBot(Bot bot, Habbo habbo) {
|
||||
HabboInfo receiverInfo = habbo == null ? Emulator.getGameEnvironment().getHabboManager().getHabboInfo(bot.getOwnerId()) : habbo.getHabboInfo();
|
||||
|
||||
if (bot != null) {
|
||||
HabboInfo receiverInfo = resolvePickupReceiver(bot, habbo);
|
||||
Room botRoom = bot.getRoom();
|
||||
if (receiverInfo == null || botRoom == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
BotPickUpEvent pickedUpEvent = new BotPickUpEvent(bot, habbo);
|
||||
Emulator.getPluginManager().fireEvent(pickedUpEvent);
|
||||
|
||||
@@ -198,8 +202,8 @@ public class BotManager {
|
||||
return;
|
||||
}
|
||||
|
||||
bot.onPickUp(habbo, receiverInfo.getCurrentRoom());
|
||||
receiverInfo.getCurrentRoom().removeBot(bot);
|
||||
bot.onPickUp(habbo, botRoom);
|
||||
botRoom.removeBot(bot);
|
||||
bot.stopFollowingHabbo();
|
||||
bot.setOwnerId(receiverInfo.getId());
|
||||
bot.setOwnerName(receiverInfo.getUsername());
|
||||
@@ -215,6 +219,14 @@ public class BotManager {
|
||||
}
|
||||
}
|
||||
|
||||
private HabboInfo resolvePickupReceiver(Bot bot, Habbo picker) {
|
||||
if (picker != null && bot.getOwnerId() == picker.getHabboInfo().getId()) {
|
||||
return picker.getHabboInfo();
|
||||
}
|
||||
|
||||
return Emulator.getGameEnvironment().getHabboManager().getHabboInfo(bot.getOwnerId());
|
||||
}
|
||||
|
||||
public Bot loadBot(ResultSet set) {
|
||||
try {
|
||||
String type = set.getString("type");
|
||||
|
||||
+8
-1
@@ -3,6 +3,7 @@ package com.eu.habbo.habbohotel.items.interactions;
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomLayout;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
@@ -133,12 +134,18 @@ public class InteractionRentableSpace extends HabboItem {
|
||||
if (habbo.getHabboStats().isRentingSpace())
|
||||
return;
|
||||
|
||||
if (habbo.getHabboInfo().getCredits() < this.rentCost())
|
||||
int cost = this.rentCost();
|
||||
boolean hasInfiniteCredits = habbo.hasPermission(Permission.ACC_INFINITE_CREDITS);
|
||||
if (!hasInfiniteCredits && habbo.getHabboInfo().getCredits() < cost)
|
||||
return;
|
||||
|
||||
if (habbo.getHabboStats().getClubExpireTimestamp() < Emulator.getIntUnixTimestamp())
|
||||
return;
|
||||
|
||||
if (!hasInfiniteCredits) {
|
||||
habbo.giveCredits(-cost);
|
||||
}
|
||||
|
||||
this.setRenterId(habbo.getHabboInfo().getId());
|
||||
this.setRenterName(habbo.getHabboInfo().getUsername());
|
||||
this.setEndTimestamp(Emulator.getIntUnixTimestamp() + (7 * 86400));
|
||||
|
||||
+15
-8
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -103,6 +103,10 @@ public class ToggleFloorItemEvent extends MessageHandler {
|
||||
|
||||
// Do not move to onClick(). Wired could trigger it.
|
||||
if (item instanceof InteractionMonsterPlantSeed) {
|
||||
if (item.getUserId() != this.client.getHabbo().getHabboInfo().getId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Emulator.getThreading().run(new QueryDeleteHabboItem(item.getId()));
|
||||
|
||||
boolean isRare = item.getBaseItem().getName().contains("rare");
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.eu.habbo.habbohotel.bots;
|
||||
|
||||
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 BotPickupOwnershipContractTest {
|
||||
private static String source() throws Exception {
|
||||
return Files.readString(Path.of("src/main/java/com/eu/habbo/habbohotel/bots/BotManager.java"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void roomOwnerPickupReturnsBotToOriginalOwner() throws Exception {
|
||||
String source = source();
|
||||
|
||||
assertTrue(source.contains("HabboInfo receiverInfo = resolvePickupReceiver(bot, habbo);"),
|
||||
"bot pickup should resolve the receiver without blindly using the picker");
|
||||
assertTrue(source.contains("private HabboInfo resolvePickupReceiver(Bot bot, Habbo picker)"),
|
||||
"bot pickup receiver logic should be centralized");
|
||||
assertTrue(source.contains("return Emulator.getGameEnvironment().getHabboManager().getHabboInfo(bot.getOwnerId());"),
|
||||
"when a room owner picks up someone else's bot, it should return to the original bot owner");
|
||||
assertTrue(source.contains("Room botRoom = bot.getRoom();"),
|
||||
"pickup should remove the bot from the bot's current room, not the receiver's current room");
|
||||
assertTrue(source.contains("botRoom.removeBot(bot);"),
|
||||
"bot removal should work even when the original owner is offline");
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class RentableSpaceChargeContractTest {
|
||||
@Test
|
||||
void rentingSpaceChargesCreditsBeforeMarkingSpaceRented() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionRentableSpace.java"));
|
||||
int rentMethod = source.indexOf("public void rent(Habbo habbo)");
|
||||
|
||||
assertTrue(rentMethod >= 0, "InteractionRentableSpace must keep explicit rent handling");
|
||||
|
||||
String rentHandling = source.substring(rentMethod, Math.min(source.length(), rentMethod + 1400));
|
||||
|
||||
assertTrue(rentHandling.contains("int cost = this.rentCost();"),
|
||||
"Rent cost must be computed once before charging");
|
||||
assertTrue(rentHandling.contains("boolean hasInfiniteCredits = habbo.hasPermission(Permission.ACC_INFINITE_CREDITS);"),
|
||||
"Renting must honor infinite-credit staff permission before charging");
|
||||
assertTrue(rentHandling.contains("!hasInfiniteCredits && habbo.getHabboInfo().getCredits() < cost"),
|
||||
"Renting must reject non-staff users without enough credits for the computed cost");
|
||||
assertTrue(rentHandling.contains("habbo.giveCredits(-cost);"),
|
||||
"Renting must deduct the computed credit cost");
|
||||
assertTrue(rentHandling.indexOf("habbo.giveCredits(-cost);") < rentHandling.indexOf("this.setRenterId"),
|
||||
"Credits must be charged before the rentable space is marked as rented");
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.eu.habbo.messages.incoming.rooms.items;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class MonsterPlantSeedOwnershipContractTest {
|
||||
@Test
|
||||
void monsterPlantSeedsCanOnlyBeRedeemedByTheirOwner() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleFloorItemEvent.java"));
|
||||
int seedBranch = source.indexOf("item instanceof InteractionMonsterPlantSeed");
|
||||
|
||||
assertTrue(seedBranch >= 0, "ToggleFloorItemEvent must keep monsterplant seed handling explicit");
|
||||
|
||||
String seedHandling = source.substring(seedBranch, Math.min(source.length(), seedBranch + 1400));
|
||||
|
||||
String ownershipGuard = "if (item.getUserId() != this.client.getHabbo().getHabboInfo().getId())";
|
||||
|
||||
assertTrue(seedHandling.contains(ownershipGuard),
|
||||
"Monsterplant seed redemption must reject callers who do not own the seed");
|
||||
assertTrue(seedHandling.contains("createMonsterplant"),
|
||||
"Monsterplant seed handling must create the pet inside the guarded branch");
|
||||
assertTrue(seedHandling.indexOf(ownershipGuard) < seedHandling.indexOf("createMonsterplant"),
|
||||
"Ownership rejection must happen before creating the pet");
|
||||
}
|
||||
}
|
||||
+29
@@ -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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user