From 64b0f499d0a9b32d1f62246616d19a64b7808563 Mon Sep 17 00:00:00 2001 From: Beny Date: Sun, 4 Oct 2020 00:25:55 +0200 Subject: [PATCH 1/5] Subscription revamp, Habbo club, HC Pay day, Clothing validation and Catalog purchase logs --- sqlupdates/DEV_TO_SUBSCRIPTION-REVAMP.sql | 88 +++++ src/main/java/com/eu/habbo/Emulator.java | 67 +++- .../java/com/eu/habbo/database/Database.java | 57 +++ .../eu/habbo/habbohotel/GameEnvironment.java | 15 + .../habbohotel/catalog/CatalogManager.java | 20 + .../catalog/CatalogPurchaseLogEntry.java | 61 +++ .../habbohotel/commands/CommandHandler.java | 1 + .../habbohotel/commands/MimicCommand.java | 3 +- .../commands/SubscriptionCommand.java | 107 +++++ .../interactions/InteractionMannequin.java | 22 +- .../football/InteractionFootballGate.java | 5 +- .../habbo/habbohotel/messenger/Messenger.java | 12 - .../habbo/habbohotel/rooms/RoomManager.java | 2 +- .../eu/habbo/habbohotel/users/HabboStats.java | 115 +++++- .../ClothingValidationManager.java | 184 +++++++++ .../users/clothingvalidation/Figuredata.java | 100 +++++ .../clothingvalidation/FiguredataPalette.java | 35 ++ .../FiguredataPaletteColor.java | 17 + .../clothingvalidation/FiguredataSettype.java | 60 +++ .../FiguredataSettypeSet.java | 21 + .../users/inventory/WardrobeComponent.java | 17 +- .../users/subscriptions/HcPayDayLogEntry.java | 64 +++ .../users/subscriptions/Subscription.java | 147 +++++++ .../subscriptions/SubscriptionHabboClub.java | 371 ++++++++++++++++++ .../subscriptions/SubscriptionManager.java | 89 +++++ .../subscriptions/SubscriptionScheduler.java | 74 ++++ .../incoming/catalog/CatalogBuyItemEvent.java | 12 +- .../catalog/RequestClubDataEvent.java | 3 + .../friends/AcceptFriendRequestEvent.java | 31 +- .../incoming/friends/FriendRequestEvent.java | 8 +- .../incoming/handshake/SecureLoginEvent.java | 14 + .../navigator/RequestCanCreateRoomEvent.java | 2 +- .../navigator/RequestCreateRoomEvent.java | 2 +- .../rooms/items/RedeemClothingEvent.java | 1 + .../incoming/users/UserSaveLookEvent.java | 3 +- .../catalog/ClubCenterDataComposer.java | 64 +-- .../friends/FriendRequestErrorComposer.java | 1 + .../outgoing/friends/FriendsComposer.java | 2 +- .../outgoing/users/UserClubComposer.java | 48 +++ .../com/eu/habbo/plugin/PluginManager.java | 56 ++- .../UserSubscriptionCreatedEvent.java | 17 + .../UserSubscriptionExpiredEvent.java | 16 + .../UserSubscriptionExtendedEvent.java | 17 + 43 files changed, 1967 insertions(+), 84 deletions(-) create mode 100644 sqlupdates/DEV_TO_SUBSCRIPTION-REVAMP.sql create mode 100644 src/main/java/com/eu/habbo/habbohotel/catalog/CatalogPurchaseLogEntry.java create mode 100644 src/main/java/com/eu/habbo/habbohotel/commands/SubscriptionCommand.java create mode 100644 src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/ClothingValidationManager.java create mode 100644 src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/Figuredata.java create mode 100644 src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataPalette.java create mode 100644 src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataPaletteColor.java create mode 100644 src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataSettype.java create mode 100644 src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataSettypeSet.java create mode 100644 src/main/java/com/eu/habbo/habbohotel/users/subscriptions/HcPayDayLogEntry.java create mode 100644 src/main/java/com/eu/habbo/habbohotel/users/subscriptions/Subscription.java create mode 100644 src/main/java/com/eu/habbo/habbohotel/users/subscriptions/SubscriptionHabboClub.java create mode 100644 src/main/java/com/eu/habbo/habbohotel/users/subscriptions/SubscriptionManager.java create mode 100644 src/main/java/com/eu/habbo/habbohotel/users/subscriptions/SubscriptionScheduler.java create mode 100644 src/main/java/com/eu/habbo/plugin/events/users/subscriptions/UserSubscriptionCreatedEvent.java create mode 100644 src/main/java/com/eu/habbo/plugin/events/users/subscriptions/UserSubscriptionExpiredEvent.java create mode 100644 src/main/java/com/eu/habbo/plugin/events/users/subscriptions/UserSubscriptionExtendedEvent.java diff --git a/sqlupdates/DEV_TO_SUBSCRIPTION-REVAMP.sql b/sqlupdates/DEV_TO_SUBSCRIPTION-REVAMP.sql new file mode 100644 index 00000000..a1686879 --- /dev/null +++ b/sqlupdates/DEV_TO_SUBSCRIPTION-REVAMP.sql @@ -0,0 +1,88 @@ +CREATE TABLE `users_subscriptions` ( + `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `user_id` int(10) UNSIGNED NULL, + `subscription_type` varchar(255) NULL, + `timestamp_start` int(10) UNSIGNED NULL, + `duration` int(10) UNSIGNED NULL, + `active` tinyint(1) NULL DEFAULT 1, + PRIMARY KEY (`id`), + INDEX `user_id`(`user_id`), + INDEX `subscription_type`(`subscription_type`), + INDEX `timestamp_start`(`timestamp_start`), + INDEX `active`(`active`) +); + +CREATE TABLE `logs_shop_purchases` ( + `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `timestamp` int(10) UNSIGNED NULL, + `user_id` int(10) UNSIGNED NULL, + `catalog_item_id` int(10) UNSIGNED NULL, + `item_ids` text DEFAULT NULL, + `catalog_name` varchar(255) NULL, + `cost_credits` int(10) NULL, + `cost_points` int(10) NULL, + `points_type` int(10) NULL, + `amount` int(10) NULL, + PRIMARY KEY (`id`), + INDEX `timestamp`(`timestamp`), + INDEX `user_id`(`user_id`) +); + +CREATE TABLE `logs_hc_payday` ( + `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `timestamp` int(10) UNSIGNED NULL, + `user_id` int(10) UNSIGNED NULL, + `hc_streak` int(10) UNSIGNED NULL, + `total_coins_spent` int(10) UNSIGNED NULL, + `reward_coins_spent` int(10) UNSIGNED NULL, + `reward_streak` int(10) UNSIGNED NULL, + `total_payout` int(10) UNSIGNED NULL, + `currency` varchar(255) NULL, + `claimed` tinyint(1) DEFAULT 0 NULL, + PRIMARY KEY (`id`), + INDEX `timestamp`(`timestamp`), + INDEX `user_id`(`user_id`) +); + +ALTER TABLE `emulator_settings` MODIFY COLUMN `value` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL AFTER `key`; +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.hc.payday.enabled', '1'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.hc.payday.next_date', '2020-10-15 00:00:00'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.hc.payday.interval', '1 month'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.hc.payday.query', 'SELECT SUM(cost_credits) AS `amount_spent` FROM `logs_shop_purchases` WHERE `user_id` = @user_id AND `timestamp` > @timestamp_start AND `timestamp` <= @timestamp_end AND `catalog_name` NOT LIKE \'CF_%\' AND `catalog_name` NOT LIKE \'CFC_%\';'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.hc.payday.streak', '7=5;30=10;60=15;90=20;180=25;365=30'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.hc.payday.currency', 'credits'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.hc.payday.percentage', '10'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.hc.payday.creditsspent_reset_on_expire', '1'); +INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('subscriptions.hc.payday.message', 'Woohoo HC Payday has arrived! You have received %amount% credits to your purse. Enjoy!') + +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.scheduler.enabled', '1'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.scheduler.interval', '10'); + +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.users.clothingvalidation.onhcexpired', '0'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.users.clothingvalidation.onlogin', '0'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.users.clothingvalidation.onchangelooks', '0'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.users.clothingvalidation.onmimic', '0'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.users.clothingvalidation.onmannequin', '0'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.users.clothingvalidation.onfballgate', '0'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('gamedata.figuredata.url', 'https://habbo.com/gamedata/figuredata/0'); + +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.users.max.friends', '300'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.users.max.friends.hc', '1100'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.users.max.rooms', '50'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.users.max.rooms.hc', '75'); + +DELETE FROM `emulator_settings` WHERE `key` = 'hotel.max.rooms.per.user'; +DELETE FROM `emulator_settings` WHERE `key` = 'hotel.max.rooms.user'; +DELETE FROM `emulator_settings` WHERE `key` = 'hotel.max.rooms.vip'; + +DELETE FROM `emulator_settings` WHERE `key` = 'max.friends'; +DELETE FROM `emulator_settings` WHERE `key` = 'max.friends'; + +ALTER TABLE `users_settings` ADD COLUMN `max_friends` int(10) NULL DEFAULT 300 AFTER `has_gotten_default_saved_searches`; +ALTER TABLE `users_settings` ADD COLUMN `max_rooms` int(10) NULL DEFAULT 50 AFTER `has_gotten_default_saved_searches`; +ALTER TABLE `users_settings` ADD COLUMN `last_hc_payday` int(10) NULL DEFAULT 0 AFTER `has_gotten_default_saved_searches`; + +ALTER TABLE `permissions` ADD COLUMN `cmd_subscription` enum('0','1') NULL DEFAULT '0' AFTER `cmd_credits`; +INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.keys.cmd_subscription', 'subscription;sub'); + +INSERT INTO users_subscriptions SELECT NULL, user_id, 'HABBO_CLUB' as `subscription_type`, UNIX_TIMESTAMP() AS `timestamp_start`, (club_expire_timestamp - UNIX_TIMESTAMP()) AS `duration`, 1 AS `active` FROM users_settings WHERE club_expire_timestamp > UNIX_TIMESTAMP(); diff --git a/src/main/java/com/eu/habbo/Emulator.java b/src/main/java/com/eu/habbo/Emulator.java index 9954c318..f5848164 100644 --- a/src/main/java/com/eu/habbo/Emulator.java +++ b/src/main/java/com/eu/habbo/Emulator.java @@ -24,10 +24,10 @@ import java.io.*; import java.security.MessageDigest; import java.sql.Timestamp; import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.Random; +import java.util.*; import java.util.concurrent.ThreadLocalRandom; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public final class Emulator { @@ -368,6 +368,65 @@ public final class Emulator { System.exit(0); } + public static int timeStringToSeconds(String timeString) { + int totalSeconds = 0; + + Matcher m = Pattern.compile("(([0-9]*) (second|minute|hour|day|week|month|year))").matcher(timeString); + Map map = new HashMap() { + { + put("second", 1); + put("minute", 60); + put("hour", 3600); + put("day", 86400); + put("week", 604800); + put("month", 2628000); + put("year", 31536000); + } + }; + + while (m.find()) { + try { + int amount = Integer.parseInt(m.group(2)); + String what = m.group(3); + totalSeconds += amount * map.get(what); + } + catch (Exception ignored) { } + } + + return totalSeconds; + } + + public static Date modifyDate(Date date, String timeString) { + int totalSeconds = 0; + + Calendar c = Calendar.getInstance(); + c.setTime(date); + + Matcher m = Pattern.compile("(([0-9]*) (second|minute|hour|day|week|month|year))").matcher(timeString); + Map map = new HashMap() { + { + put("second", Calendar.SECOND); + put("minute", Calendar.MINUTE); + put("hour", Calendar.HOUR); + put("day", Calendar.DAY_OF_MONTH); + put("week", Calendar.WEEK_OF_MONTH); + put("month", Calendar.MONTH); + put("year", Calendar.YEAR); + } + }; + + while (m.find()) { + try { + int amount = Integer.parseInt(m.group(2)); + String what = m.group(3); + c.add(map.get(what), amount); + } + catch (Exception ignored) { } + } + + return c.getTime(); + } + private static String dateToUnixTimestamp(Date date) { String res = ""; Date aux = stringToDate("1970-01-01 00:00:00"); @@ -378,7 +437,7 @@ public final class Emulator { return res + seconds; } - private static Date stringToDate(String date) { + public static Date stringToDate(String date) { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date res = null; try { diff --git a/src/main/java/com/eu/habbo/database/Database.java b/src/main/java/com/eu/habbo/database/Database.java index c37a524a..00be0fee 100644 --- a/src/main/java/com/eu/habbo/database/Database.java +++ b/src/main/java/com/eu/habbo/database/Database.java @@ -3,9 +3,18 @@ package com.eu.habbo.database; import com.eu.habbo.Emulator; import com.eu.habbo.core.ConfigurationManager; import com.zaxxer.hikari.HikariDataSource; +import gnu.trove.map.hash.THashMap; +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.SQLException; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + public class Database { private static final Logger LOGGER = LoggerFactory.getLogger(Database.class); @@ -53,4 +62,52 @@ public class Database { public DatabasePool getDatabasePool() { return this.databasePool; } + + public static PreparedStatement preparedStatementWithParams(Connection connection, String query, THashMap queryParams) throws SQLException { + THashMap params = new THashMap(); + THashSet quotedParams = new THashSet<>(); + + for(String key : queryParams.keySet()) { + quotedParams.add(Pattern.quote(key)); + } + + String regex = "(" + String.join("|", quotedParams) + ")"; + + Matcher m = Pattern.compile(regex).matcher(query); + + int i = 1; + + while (m.find()) { + try { + params.put(i, queryParams.get(m.group(1))); + i++; + } + catch (Exception ignored) { } + } + + PreparedStatement statement = connection.prepareStatement(query.replaceAll(regex, "?")); + + for(Map.Entry set : params.entrySet()) { + if(set.getValue().getClass() == String.class) { + statement.setString(set.getKey(), (String)set.getValue()); + } + else if(set.getValue().getClass() == Integer.class) { + statement.setInt(set.getKey(), (Integer)set.getValue()); + } + else if(set.getValue().getClass() == Double.class) { + statement.setDouble(set.getKey(), (Double)set.getValue()); + } + else if(set.getValue().getClass() == Float.class) { + statement.setFloat(set.getKey(), (Float)set.getValue()); + } + else if(set.getValue().getClass() == Long.class) { + statement.setLong(set.getKey(), (Long)set.getValue()); + } + else { + statement.setObject(set.getKey(), set.getValue()); + } + } + + return statement; + } } diff --git a/src/main/java/com/eu/habbo/habbohotel/GameEnvironment.java b/src/main/java/com/eu/habbo/habbohotel/GameEnvironment.java index 63a956bb..a614cb05 100644 --- a/src/main/java/com/eu/habbo/habbohotel/GameEnvironment.java +++ b/src/main/java/com/eu/habbo/habbohotel/GameEnvironment.java @@ -20,6 +20,8 @@ import com.eu.habbo.habbohotel.pets.PetManager; import com.eu.habbo.habbohotel.polls.PollManager; import com.eu.habbo.habbohotel.rooms.RoomManager; import com.eu.habbo.habbohotel.users.HabboManager; +import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionManager; +import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionScheduler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,6 +33,8 @@ public class GameEnvironment { public PixelScheduler pixelScheduler; public PointsScheduler pointsScheduler; public GotwPointsScheduler gotwPointsScheduler; + public SubscriptionScheduler subscriptionScheduler; + private HabboManager habboManager; private NavigatorManager navigatorManager; private GuildManager guildManager; @@ -49,6 +53,7 @@ public class GameEnvironment { private WordFilter wordFilter; private CraftingManager craftingManager; private PollManager pollManager; + private SubscriptionManager subscriptionManager; public void load() throws Exception { LOGGER.info("GameEnvironment -> Loading..."); @@ -86,6 +91,11 @@ public class GameEnvironment { this.gotwPointsScheduler = new GotwPointsScheduler(); Emulator.getThreading().run(this.gotwPointsScheduler); + this.subscriptionManager = new SubscriptionManager(); + this.subscriptionManager.init(); + + this.subscriptionScheduler = new SubscriptionScheduler(); + Emulator.getThreading().run(this.subscriptionScheduler); LOGGER.info("GameEnvironment -> Loaded!"); } @@ -103,6 +113,7 @@ public class GameEnvironment { this.roomManager.dispose(); this.itemManager.dispose(); this.hotelViewManager.dispose(); + this.subscriptionManager.dispose(); LOGGER.info("GameEnvironment -> Disposed!"); } @@ -191,4 +202,8 @@ public class GameEnvironment { public GotwPointsScheduler getGotwPointsScheduler() { return this.gotwPointsScheduler; } + + public SubscriptionManager getSubscriptionManager() { + return this.subscriptionManager; + } } diff --git a/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogManager.java b/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogManager.java index 255031af..87649d94 100644 --- a/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogManager.java +++ b/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogManager.java @@ -1121,6 +1121,26 @@ public class CatalogManager { habbo.getClient().sendResponse(new PurchaseOKComposer(purchasedEvent.catalogItem)); habbo.getClient().sendResponse(new InventoryRefreshComposer()); + THashSet itemIds = new THashSet<>(); + + for(HabboItem ix : purchasedEvent.itemsList) { + itemIds.add(ix.getId() + ""); + } + + if(!free) { + Emulator.getThreading().run(new CatalogPurchaseLogEntry( + Emulator.getIntUnixTimestamp(), + purchasedEvent.habbo.getHabboInfo().getId(), + purchasedEvent.catalogItem != null ? purchasedEvent.catalogItem.getId() : 0, + String.join(";", itemIds), + purchasedEvent.catalogItem != null ? purchasedEvent.catalogItem.getName() : "", + purchasedEvent.totalCredits, + purchasedEvent.totalPoints, + item != null ? item.getPointsType() : 0, + amount + )); + } + } catch (Exception e) { LOGGER.error("Exception caught", e); habbo.getClient().sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR)); diff --git a/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogPurchaseLogEntry.java b/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogPurchaseLogEntry.java new file mode 100644 index 00000000..5f6febd7 --- /dev/null +++ b/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogPurchaseLogEntry.java @@ -0,0 +1,61 @@ +package com.eu.habbo.habbohotel.catalog; + +import com.eu.habbo.Emulator; +import com.eu.habbo.core.DatabaseLoggable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public class CatalogPurchaseLogEntry implements Runnable, DatabaseLoggable { + + private static final Logger LOGGER = LoggerFactory.getLogger(CatalogPurchaseLogEntry.class); + private static final String QUERY = "INSERT INTO `logs_shop_purchases` (timestamp, user_id, catalog_item_id, item_ids, catalog_name, cost_credits, cost_points, points_type, amount) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + private final int timestamp; + private final int userId; + private final int catalogItemId; + private final String itemIds; + private final String catalogName; + private final int costCredits; + private final int costPoints; + private final int pointsType; + private final int amount; + + public CatalogPurchaseLogEntry(int timestamp, int userId, int catalogItemId, String itemIds, String catalogName, int costCredits, int costPoints, int pointsType, int amount) { + this.timestamp = timestamp; + this.userId = userId; + this.catalogItemId = catalogItemId; + this.itemIds = itemIds; + this.catalogName = catalogName; + this.costCredits = costCredits; + this.costPoints = costPoints; + this.pointsType = pointsType; + this.amount = amount; + } + + @Override + public String getQuery() { + return QUERY; + } + + @Override + public void log(PreparedStatement statement) throws SQLException { + statement.setInt(1, this.timestamp); + statement.setInt(2, this.userId); + statement.setInt(3, this.catalogItemId); + statement.setString(4, this.itemIds); + statement.setString(5, this.catalogName); + statement.setInt(6, this.costCredits); + statement.setInt(7, this.costPoints); + statement.setInt(8, this.pointsType); + statement.setInt(9, this.amount); + statement.addBatch(); + } + + @Override + public void run() { + Emulator.getDatabaseLogger().store(this); + } +} diff --git a/src/main/java/com/eu/habbo/habbohotel/commands/CommandHandler.java b/src/main/java/com/eu/habbo/habbohotel/commands/CommandHandler.java index 16d2c5c9..d3fe03bf 100644 --- a/src/main/java/com/eu/habbo/habbohotel/commands/CommandHandler.java +++ b/src/main/java/com/eu/habbo/habbohotel/commands/CommandHandler.java @@ -285,6 +285,7 @@ public class CommandHandler { addCommand(new UpdateYoutubePlaylistsCommand()); addCommand(new AddYoutubePlaylistCommand()); addCommand(new SoftKickCommand()); + addCommand(new SubscriptionCommand()); addCommand(new TestCommand()); } diff --git a/src/main/java/com/eu/habbo/habbohotel/commands/MimicCommand.java b/src/main/java/com/eu/habbo/habbohotel/commands/MimicCommand.java index 123f1db4..13d72c30 100644 --- a/src/main/java/com/eu/habbo/habbohotel/commands/MimicCommand.java +++ b/src/main/java/com/eu/habbo/habbohotel/commands/MimicCommand.java @@ -6,6 +6,7 @@ import com.eu.habbo.habbohotel.permissions.Permission; import com.eu.habbo.habbohotel.rooms.RoomChatMessageBubbles; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboGender; +import com.eu.habbo.habbohotel.users.clothingvalidation.ClothingValidationManager; import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDataComposer; import com.eu.habbo.messages.outgoing.users.UserDataComposer; import com.eu.habbo.util.figure.FigureUtil; @@ -35,7 +36,7 @@ public class MimicCommand extends Command { gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_mimic.forbidden_clothing"), RoomChatMessageBubbles.ALERT); return true; } else { - gameClient.getHabbo().getHabboInfo().setLook(habbo.getHabboInfo().getLook()); + gameClient.getHabbo().getHabboInfo().setLook(ClothingValidationManager.VALIDATE_ON_MIMIC ? ClothingValidationManager.validateLook(gameClient.getHabbo(), habbo.getHabboInfo().getLook(), habbo.getHabboInfo().getGender().name()) : habbo.getHabboInfo().getLook()); gameClient.getHabbo().getHabboInfo().setGender(habbo.getHabboInfo().getGender()); gameClient.sendResponse(new UserDataComposer(gameClient.getHabbo())); gameClient.getHabbo().getHabboInfo().getCurrentRoom().sendComposer(new RoomUserDataComposer(gameClient.getHabbo()).compose()); diff --git a/src/main/java/com/eu/habbo/habbohotel/commands/SubscriptionCommand.java b/src/main/java/com/eu/habbo/habbohotel/commands/SubscriptionCommand.java new file mode 100644 index 00000000..fbf32b54 --- /dev/null +++ b/src/main/java/com/eu/habbo/habbohotel/commands/SubscriptionCommand.java @@ -0,0 +1,107 @@ +package com.eu.habbo.habbohotel.commands; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.gameclients.GameClient; +import com.eu.habbo.habbohotel.rooms.RoomChatMessageBubbles; +import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.habbohotel.users.HabboInfo; +import com.eu.habbo.habbohotel.users.HabboManager; +import com.eu.habbo.habbohotel.users.subscriptions.Subscription; + +/** + * @author Beny + */ +public class SubscriptionCommand extends Command { + public SubscriptionCommand() { + super("cmd_subscription", Emulator.getTexts().getValue("commands.keys.cmd_subscription").split(";")); + } + + /** + * Allows you to give/extend/remove subscription on a given user. + * + * Parameters: + * [username] = Username of user to execute command on + * [type] = Subscription type (e.g. HABBO_CLUB) + * [add|remove] = Use add or remove to increase/decrease sub duration + * [time] = Time string e.g. "1 week", "18 days", "4 minutes". Can be complex e.g. "1 month 5 days 2 minutes" + * + * Examples: + * :sub Beny habbo_club add 1 month - adds 1 month of HABBO_CLUB subscription duration on the user Beny + * :sub Beny builders_club add 1 month - adds 1 month of BUILDERS_CLUB subscription duration on the user Beny + * :sub Beny habbo_club remove 3 days - removes 3 days of HABBO_CLUB subscription duration on the user Beny + * :sub Beny habbo_club remove - removes all remaining time from the HABBO_CLUB subscription (expires it) on the user Beny + * + * @param gameClient Client that executed the command + * @param params Command parameters + * @return Boolean indicating success + * @throws Exception Exception + */ + @Override + public boolean handle(GameClient gameClient, String[] params) throws Exception { + if (params.length >= 4) { + HabboInfo info = HabboManager.getOfflineHabboInfo(params[1]); + + if (info != null) { + Habbo habbo = Emulator.getGameServer().getGameClientManager().getHabbo(params[1]); + + String subscription = params[2].toUpperCase(); + String action = params[3]; + + StringBuilder message = new StringBuilder(); + if (params.length > 4) { + for (int i = 4; i < params.length; i++) { + message.append(params[i]).append(" "); + } + } + + if(!Emulator.getGameEnvironment().getSubscriptionManager().types.containsKey(subscription)) { + gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.type_not_found", "%subscription% is not a valid subscription type").replace("%subscription%", subscription), RoomChatMessageBubbles.ALERT); + return true; + } + + if(action.equalsIgnoreCase("add") || action.equalsIgnoreCase("+") || action.equalsIgnoreCase("a")) { + int timeToAdd = Emulator.timeStringToSeconds(message.toString()); + + if(timeToAdd < 1) { + gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.invalid_params", "Invalid time span, try: x minutes/days/weeks/months"), RoomChatMessageBubbles.ALERT); + return true; + } + + habbo.getHabboStats().createSubscription(subscription, timeToAdd); + gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.success_add_time", "Successfully added %time% seconds to %subscription% on %user%").replace("%time%", timeToAdd + "").replace("%user%", params[1]).replace("%subscription%", subscription), RoomChatMessageBubbles.ALERT); + } + + else if(action.equalsIgnoreCase("remove") || action.equalsIgnoreCase("-") || action.equalsIgnoreCase("r")) { + Subscription s = habbo.getHabboStats().getSubscription(subscription); + + if (s == null) { + gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.user_not_have", "%user% does not have the %subscription% subscription").replace("%user%", params[1]).replace("%subscription%", subscription), RoomChatMessageBubbles.ALERT); + return true; + } + + if(message.length() != 0) { + int timeToRemove = Emulator.timeStringToSeconds(message.toString()); + + if (timeToRemove < 1) { + gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.invalid_params", "Invalid time span, try: x minutes/days/weeks/months"), RoomChatMessageBubbles.ALERT); + return true; + } + + s.addDuration(timeToRemove); + gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.success_remove_time", "Successfully removed %time% seconds from %subscription% on %user%").replace("%time%", timeToRemove + "").replace("%user%", params[1]).replace("%subscription%", subscription), RoomChatMessageBubbles.ALERT); + } + else { + s.addDuration(-s.getRemaining()); + gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.success_remove_sub", "Successfully removed %subscription% sub from %user%").replace("%user%", params[1]).replace("%subscription%", subscription), RoomChatMessageBubbles.ALERT); + } + } + + } else { + gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.user_not_found", "%user% was not found").replace("%user%", params[1]), RoomChatMessageBubbles.ALERT); + } + } else { + gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.invalid_params", "Invalid command format"), RoomChatMessageBubbles.ALERT); + } + return true; + } +} diff --git a/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionMannequin.java b/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionMannequin.java index cc291a67..9036b625 100644 --- a/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionMannequin.java +++ b/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionMannequin.java @@ -6,6 +6,7 @@ import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.users.clothingvalidation.ClothingValidationManager; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDataComposer; import com.eu.habbo.messages.outgoing.users.UserDataComposer; @@ -60,7 +61,17 @@ public class InteractionMannequin extends HabboItem { @Override public void onClick(GameClient client, Room room, Object[] objects) throws Exception { - String lookCode = this.getExtradata().split(":")[1]; + String[] data = this.getExtradata().split(":"); + + if(data.length < 2) + return; + + String gender = data[0]; + String figure = data[1]; + + if (gender.isEmpty() || figure.isEmpty() || (!gender.equalsIgnoreCase("m") && !gender.equalsIgnoreCase("f")) || !client.getHabbo().getHabboInfo().getGender().name().equalsIgnoreCase(gender)) + return; + String newFigure = ""; for (String playerFigurePart : client.getHabbo().getHabboInfo().getLook().split("\\.")) { @@ -68,8 +79,7 @@ public class InteractionMannequin extends HabboItem { newFigure += playerFigurePart + "."; } - if (lookCode.isEmpty()) return; - String newFigureParts = lookCode; + String newFigureParts = figure; for (String newFigurePart : newFigureParts.split("\\.")) { if (newFigurePart.startsWith("hd")) @@ -78,12 +88,12 @@ public class InteractionMannequin extends HabboItem { if (newFigureParts.equals("")) return; - final String figure = newFigure + newFigureParts; + String newLook = newFigure + newFigureParts; - if (figure.length() > 512) + if (newLook.length() > 512) return; - client.getHabbo().getHabboInfo().setLook(figure); + client.getHabbo().getHabboInfo().setLook(ClothingValidationManager.VALIDATE_ON_MANNEQUIN ? ClothingValidationManager.validateLook(client.getHabbo(), newLook, client.getHabbo().getHabboInfo().getGender().name()) : newLook); room.sendComposer(new RoomUserDataComposer(client.getHabbo()).compose()); client.sendResponse(new UserDataComposer(client.getHabbo())); } diff --git a/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/football/InteractionFootballGate.java b/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/football/InteractionFootballGate.java index 73db3741..265b4cb9 100644 --- a/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/football/InteractionFootballGate.java +++ b/src/main/java/com/eu/habbo/habbohotel/items/interactions/games/football/InteractionFootballGate.java @@ -7,6 +7,7 @@ import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboGender; import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.users.clothingvalidation.ClothingValidationManager; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDataComposer; import com.eu.habbo.messages.outgoing.users.UpdateUserLookComposer; @@ -120,7 +121,7 @@ public class InteractionFootballGate extends HabboItem { UserSavedLookEvent lookEvent = new UserSavedLookEvent(habbo, habbo.getHabboInfo().getGender(), oldlook); Emulator.getPluginManager().fireEvent(lookEvent); if (!lookEvent.isCancelled()) { - habbo.getHabboInfo().setLook(lookEvent.newLook); + habbo.getHabboInfo().setLook(ClothingValidationManager.VALIDATE_ON_FBALLGATE ? ClothingValidationManager.validateLook(habbo, lookEvent.newLook, lookEvent.gender.name()) : lookEvent.newLook); Emulator.getThreading().run(habbo.getHabboInfo()); habbo.getClient().sendResponse(new UpdateUserLookComposer(habbo)); room.sendComposer(new RoomUserDataComposer(habbo).compose()); @@ -134,7 +135,7 @@ public class InteractionFootballGate extends HabboItem { Emulator.getPluginManager().fireEvent(lookEvent); if (!lookEvent.isCancelled()) { habbo.getHabboStats().cache.put(CACHE_KEY, habbo.getHabboInfo().getLook()); - habbo.getHabboInfo().setLook(lookEvent.newLook); + habbo.getHabboInfo().setLook(ClothingValidationManager.VALIDATE_ON_FBALLGATE ? ClothingValidationManager.validateLook(habbo, lookEvent.newLook, lookEvent.gender.name()) : lookEvent.newLook); Emulator.getThreading().run(habbo.getHabboInfo()); habbo.getClient().sendResponse(new UpdateUserLookComposer(habbo)); room.sendComposer(new RoomUserDataComposer(habbo).compose()); diff --git a/src/main/java/com/eu/habbo/habbohotel/messenger/Messenger.java b/src/main/java/com/eu/habbo/habbohotel/messenger/Messenger.java index 59c6083a..db3c357c 100644 --- a/src/main/java/com/eu/habbo/habbohotel/messenger/Messenger.java +++ b/src/main/java/com/eu/habbo/habbohotel/messenger/Messenger.java @@ -167,18 +167,6 @@ public class Messenger { return map; } - public static int friendLimit(Habbo habbo) { - if (habbo.hasPermission("acc_infinite_friends")) { - return Integer.MAX_VALUE; - } - - if (habbo.getHabboStats().hasActiveClub()) { - return MAXIMUM_FRIENDS_HC; - } - - return MAXIMUM_FRIENDS; - } - public static void checkFriendSizeProgress(Habbo habbo) { int progress = habbo.getHabboStats().getAchievementProgress(Emulator.getGameEnvironment().getAchievementManager().getAchievement("FriendListSize")); diff --git a/src/main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java b/src/main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java index d530de77..2590f202 100644 --- a/src/main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java +++ b/src/main/java/com/eu/habbo/habbohotel/rooms/RoomManager.java @@ -66,7 +66,7 @@ public class RoomManager { private static final int page = 0; //Configuration. Loaded from database & updated accordingly. public static int MAXIMUM_ROOMS_USER = 25; - public static int MAXIMUM_ROOMS_VIP = 35; + public static int MAXIMUM_ROOMS_HC = 35; public static int HOME_ROOM_ID = 0; public static boolean SHOW_PUBLIC_IN_POPULAR_TAB = false; private final THashMap roomCategories; diff --git a/src/main/java/com/eu/habbo/habbohotel/users/HabboStats.java b/src/main/java/com/eu/habbo/habbohotel/users/HabboStats.java index d7b03956..edf706a4 100644 --- a/src/main/java/com/eu/habbo/habbohotel/users/HabboStats.java +++ b/src/main/java/com/eu/habbo/habbohotel/users/HabboStats.java @@ -9,22 +9,22 @@ import com.eu.habbo.habbohotel.catalog.CatalogItem; import com.eu.habbo.habbohotel.rooms.RoomChatMessageBubbles; import com.eu.habbo.habbohotel.rooms.RoomTrade; import com.eu.habbo.habbohotel.users.cache.HabboOfferPurchase; +import com.eu.habbo.habbohotel.users.subscriptions.Subscription; +import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionManager; +import com.eu.habbo.plugin.events.users.subscriptions.UserSubscriptionCreatedEvent; +import com.eu.habbo.plugin.events.users.subscriptions.UserSubscriptionExtendedEvent; import gnu.trove.list.array.TIntArrayList; import gnu.trove.map.TIntObjectMap; import gnu.trove.map.hash.THashMap; import gnu.trove.map.hash.TIntObjectHashMap; +import gnu.trove.set.hash.THashSet; import gnu.trove.stack.array.TIntArrayStack; 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.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.lang.reflect.Constructor; +import java.sql.*; +import java.util.*; import java.util.concurrent.atomic.AtomicInteger; public class HabboStats implements Runnable { @@ -93,6 +93,11 @@ public class HabboStats implements Runnable { private boolean allowTrade; private int clubExpireTimestamp; private int muteEndTime; + public int maxFriends; + public int maxRooms; + public int lastHCPayday; + public int hcMessageLastModified = Emulator.getIntUnixTimestamp(); + public THashSet subscriptions; private HabboStats(ResultSet set, HabboInfo habboInfo) throws SQLException { this.cache = new THashMap<>(0); @@ -142,9 +147,14 @@ public class HabboStats implements Runnable { this.forumPostsCount = set.getInt("forums_post_count"); this.uiFlags = set.getInt("ui_flags"); this.hasGottenDefaultSavedSearches = set.getInt("has_gotten_default_saved_searches") == 1; + this.maxFriends = set.getInt("max_friends"); + this.maxRooms = set.getInt("max_rooms"); + this.lastHCPayday = set.getInt("last_hc_payday"); this.nuxReward = this.nux; + this.subscriptions = Emulator.getGameEnvironment().getSubscriptionManager().getSubscriptionsForUser(this.habboInfo.getId()); + try (PreparedStatement statement = set.getStatement().getConnection().prepareStatement("SELECT * FROM user_window_settings WHERE user_id = ? LIMIT 1")) { statement.setInt(1, this.habboInfo.getId()); try (ResultSet nSet = statement.executeQuery()) { @@ -306,7 +316,7 @@ public class HabboStats implements Runnable { int onlineTime = Emulator.getIntUnixTimestamp() - onlineTimeLast; try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) { - try (PreparedStatement statement = connection.prepareStatement("UPDATE users_settings SET achievement_score = ?, respects_received = ?, respects_given = ?, daily_respect_points = ?, block_following = ?, block_friendrequests = ?, online_time = online_time + ?, guild_id = ?, daily_pet_respect_points = ?, club_expire_timestamp = ?, login_streak = ?, rent_space_id = ?, rent_space_endtime = ?, volume_system = ?, volume_furni = ?, volume_trax = ?, block_roominvites = ?, old_chat = ?, block_camera_follow = ?, chat_color = ?, hof_points = ?, block_alerts = ?, talent_track_citizenship_level = ?, talent_track_helpers_level = ?, ignore_bots = ?, ignore_pets = ?, nux = ?, mute_end_timestamp = ?, allow_name_change = ?, perk_trade = ?, can_trade = ?, `forums_post_count` = ?, ui_flags = ?, has_gotten_default_saved_searches = ? WHERE user_id = ? LIMIT 1")) { + try (PreparedStatement statement = connection.prepareStatement("UPDATE users_settings SET achievement_score = ?, respects_received = ?, respects_given = ?, daily_respect_points = ?, block_following = ?, block_friendrequests = ?, online_time = online_time + ?, guild_id = ?, daily_pet_respect_points = ?, club_expire_timestamp = ?, login_streak = ?, rent_space_id = ?, rent_space_endtime = ?, volume_system = ?, volume_furni = ?, volume_trax = ?, block_roominvites = ?, old_chat = ?, block_camera_follow = ?, chat_color = ?, hof_points = ?, block_alerts = ?, talent_track_citizenship_level = ?, talent_track_helpers_level = ?, ignore_bots = ?, ignore_pets = ?, nux = ?, mute_end_timestamp = ?, allow_name_change = ?, perk_trade = ?, can_trade = ?, `forums_post_count` = ?, ui_flags = ?, has_gotten_default_saved_searches = ?, max_friends = ?, max_rooms = ?, last_hc_payday = ? WHERE user_id = ? LIMIT 1")) { statement.setInt(1, this.achievementScore); statement.setInt(2, this.respectPointsReceived); statement.setInt(3, this.respectPointsGiven); @@ -341,7 +351,10 @@ public class HabboStats implements Runnable { statement.setInt(32, this.forumPostsCount); statement.setInt(33, this.uiFlags); statement.setInt(34, this.hasGottenDefaultSavedSearches ? 1 : 0); - statement.setInt(35, this.habboInfo.getId()); + statement.setInt(35, this.maxFriends); + statement.setInt(36, this.maxRooms); + statement.setInt(37, this.lastHCPayday); + statement.setInt(38, this.habboInfo.getId()); statement.executeUpdate(); } @@ -441,16 +454,92 @@ public class HabboStats implements Runnable { return this.rentedTimeEnd >= Emulator.getIntUnixTimestamp(); } + public Subscription getSubscription(String subscriptionType) { + for(Subscription subscription : subscriptions) { + if(subscription.getSubscriptionType().equalsIgnoreCase(subscriptionType) && subscription.isActive() && subscription.getRemaining() > 0) { + return subscription; + } + } + return null; + } + + public boolean hasSubscription(String subscriptionType) { + Subscription subscription = getSubscription(subscriptionType); + return subscription != null; + } + + public int getSubscriptionExpireTimestamp(String subscriptionType) { + Subscription subscription = getSubscription(subscriptionType); + + if(subscription == null) + return 0; + + return subscription.getTimestampEnd(); + } + + public Subscription createSubscription(String subscriptionType, int duration) { + Subscription subscription = getSubscription(subscriptionType); + + if(subscription != null) { + if (!Emulator.getPluginManager().fireEvent(new UserSubscriptionExtendedEvent(this.habboInfo.getId(), subscription, duration)).isCancelled()) { + subscription.addDuration(duration); + subscription.onExtended(duration); + } + return subscription; + } + + if (!Emulator.getPluginManager().fireEvent(new UserSubscriptionCreatedEvent(this.habboInfo.getId(), subscriptionType, duration)).isCancelled()) { + int startTimestamp = Emulator.getIntUnixTimestamp(); + try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO `users_subscriptions` (`user_id`, `subscription_type`, `timestamp_start`, `duration`, `active`) VALUES (?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS)) { + statement.setInt(1, this.habboInfo.getId()); + statement.setString(2, subscriptionType); + statement.setInt(3, startTimestamp); + statement.setInt(4, duration); + statement.setInt(5, 1); + statement.execute(); + try (ResultSet set = statement.getGeneratedKeys()) { + if (set.next()) { + Class subClazz = Emulator.getGameEnvironment().getSubscriptionManager().getSubscriptionClass(subscriptionType); + try { + Constructor c = subClazz.getConstructor(Integer.class, Integer.class, String.class, Integer.class, Integer.class, Boolean.class); + c.setAccessible(true); + Subscription sub = c.newInstance(set.getInt(1), this.habboInfo.getId(), subscriptionType, startTimestamp, duration, true); + sub.onCreated(); + this.subscriptions.add(sub); + return sub; + } + catch (Exception e) { + LOGGER.error("Caught exception", e); + } + } + } + } catch (SQLException e) { + LOGGER.error("Caught SQL exception", e); + } + } + + return null; + } + public int getClubExpireTimestamp() { - return this.clubExpireTimestamp; + return getSubscriptionExpireTimestamp(Subscription.HABBO_CLUB); } public void setClubExpireTimestamp(int clubExpireTimestamp) { - this.clubExpireTimestamp = clubExpireTimestamp; + Subscription subscription = getSubscription(Subscription.HABBO_CLUB); + int duration = clubExpireTimestamp - Emulator.getIntUnixTimestamp(); + + if(subscription != null) { + duration = clubExpireTimestamp - subscription.getTimestampStart(); + } + + if(duration > 0) { + createSubscription(Subscription.HABBO_CLUB, duration); + } } public boolean hasActiveClub() { - return this.clubExpireTimestamp > Emulator.getIntUnixTimestamp(); + return hasSubscription(Subscription.HABBO_CLUB); } public THashMap getAchievementProgress() { diff --git a/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/ClothingValidationManager.java b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/ClothingValidationManager.java new file mode 100644 index 00000000..ff4e2664 --- /dev/null +++ b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/ClothingValidationManager.java @@ -0,0 +1,184 @@ +package com.eu.habbo.habbohotel.users.clothingvalidation; + +import com.eu.habbo.habbohotel.users.Habbo; +import gnu.trove.TIntCollection; +import gnu.trove.set.hash.TIntHashSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.regex.Pattern; + +public class ClothingValidationManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(ClothingValidationManager.class); + + public static String FIGUREDATA_URL = ""; + public static boolean VALIDATE_ON_HC_EXPIRE = false; + public static boolean VALIDATE_ON_LOGIN = false; + public static boolean VALIDATE_ON_CHANGE_LOOKS = false; + public static boolean VALIDATE_ON_MIMIC = false; + public static boolean VALIDATE_ON_MANNEQUIN = false; + public static boolean VALIDATE_ON_FBALLGATE = false; + + private static final Figuredata FIGUREDATA = new Figuredata(); + + /** + * Parses the new figuredata.xml file + * @param newUrl URI of figuredata.xml file. Can be a file path or URL + */ + public static void reloadFiguredata(String newUrl) { + try { + FIGUREDATA.parseXML(newUrl); + } catch (Exception e) { + VALIDATE_ON_HC_EXPIRE = false; + VALIDATE_ON_LOGIN = false; + VALIDATE_ON_CHANGE_LOOKS = false; + VALIDATE_ON_MIMIC = false; + VALIDATE_ON_MANNEQUIN = false; + VALIDATE_ON_FBALLGATE = false; + LOGGER.error("Caught exception", e); + } + } + + /** + * Validates a figure string on a given user + * @param habbo User to validate + * @return Cleaned figure string + */ + public static String validateLook(Habbo habbo) { + return validateLook(habbo.getHabboInfo().getLook(), habbo.getHabboInfo().getGender().name(), habbo.getHabboStats().hasActiveClub(), habbo.getInventory().getWardrobeComponent().getClothingSets()); + } + + /** + * Validates a given figure string and gender on a given user + * @param habbo User to validate + * @param look Figure string + * @param gender Gender (M/F) + * @return Cleaned figure string + */ + public static String validateLook(Habbo habbo, String look, String gender) { + return validateLook(look, gender, habbo.getHabboStats().hasActiveClub(), habbo.getInventory().getWardrobeComponent().getClothingSets()); + } + + /** + * Validates a given figure string against a given gender + * @param look Figure string + * @param gender Gender (M/F) + * @return Cleaned figure string + */ + public static String validateLook(String look, String gender) { + return validateLook(look, gender, false, new TIntHashSet()); + } + + /** + * Validates a given figure string against a given gender with club clothing option + * @param look Figure string + * @param gender Gender (M/F) + * @param isHC Boolean indicating if club clothing is permitted + * @return Cleaned figure string + */ + public static String validateLook(String look, String gender, boolean isHC) { + return validateLook(look, gender, isHC, new TIntHashSet()); + } + + /** + * Validates a figure string with all available options + * @param look Figure string + * @param gender Gender (M/F) + * @param isHC Boolean indicating if club clothing is permitted + * @param ownedClothing Array of owned clothing set IDs. If sellable and setId not in this array clothing will be removed + * @return Cleaned figure string + */ + public static String validateLook(String look, String gender, boolean isHC, TIntCollection ownedClothing) { + if(FIGUREDATA.palettes.size() == 0 || FIGUREDATA.settypes.size() == 0) + return look; + + String[] newLookParts = look.split(Pattern.quote(".")); + ArrayList lookParts = new ArrayList<>(); + + for(String lookpart : newLookParts) { + if(lookpart.contains("-")) { + try { + String[] data = lookpart.split(Pattern.quote("-")); + if (data.length > 1) { + FiguredataSettype settype = FIGUREDATA.settypes.get(data[0]); + if (settype == null) { + throw new Exception("Set type " + data[0] + " does not exist"); + } + + FiguredataPalette palette = FIGUREDATA.palettes.get(settype.paletteId); + if (palette == null) { + throw new Exception("Palette " + settype.paletteId + " does not exist"); + } + + int setId; + FiguredataSettypeSet set; + + setId = Integer.parseInt(data[1]); + set = settype.getSet(setId); + if (set == null) + throw new Exception("Set " + setId + " does not exist in SetType"); + + if ((set.club && !isHC) || !set.selectable || (set.sellable && !ownedClothing.contains(set.id))) { + if(gender.equalsIgnoreCase("M") && !isHC && !settype.mandatoryMale0) + continue; + + if(gender.equalsIgnoreCase("F") && !isHC && !settype.mandatoryFemale0) + continue; + + if(gender.equalsIgnoreCase("M") && isHC && !settype.mandatoryMale1) + continue; + + if(gender.equalsIgnoreCase("F") && isHC && !settype.mandatoryFemale1) + continue; + + set = settype.getFirstNonHCSetForGender(gender); + setId = set.id; + } + + ArrayList dataParts = new ArrayList<>(); + + int color1 = -1; + int color2 = -1; + + if (data.length > 2 && set.colorable) { + color1 = Integer.parseInt(data[2]); + FiguredataPaletteColor color = palette.getColor(color1); + if (color == null || (color.club && !isHC)) { + color1 = palette.getFirstNonHCColor().id; + } + } + + if (data.length > 3 && set.colorable) { + color2 = Integer.parseInt(data[3]); + FiguredataPaletteColor color = palette.getColor(color2); + if (color == null || (color.club && !isHC)) { + color2 = palette.getFirstNonHCColor().id; + } + } + + dataParts.add(settype.type); + dataParts.add("" + setId); + + if (color1 > -1) { + dataParts.add("" + color1); + } + + if (color2 > -1) { + dataParts.add("" + color2); + } + + lookParts.add(String.join("-", dataParts)); + } + } + catch (Exception e) { + //habbo.alert(e.getMessage()); + } + } + } + + return String.join(".", lookParts); + } + +} diff --git a/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/Figuredata.java b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/Figuredata.java new file mode 100644 index 00000000..11eadc9f --- /dev/null +++ b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/Figuredata.java @@ -0,0 +1,100 @@ +package com.eu.habbo.habbohotel.users.clothingvalidation; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.util.Map; +import java.util.TreeMap; + +public class Figuredata { + public Map palettes; + public Map settypes; + + public Figuredata() { + palettes = new TreeMap<>(); + settypes = new TreeMap<>(); + } + + /** + * Parses the figuredata.xml file + * @param uri URI to the figuredata.xml file + * @throws ParserConfigurationException + * @throws IOException + * @throws SAXException + */ + public void parseXML(String uri) throws ParserConfigurationException, IOException, SAXException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setValidating(false); + factory.setIgnoringElementContentWhitespace(true); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.parse(uri); + + Element rootElement = document.getDocumentElement(); + NodeList palettesList = document.getElementsByTagName("colors").item(0).getChildNodes(); + NodeList settypesList = document.getElementsByTagName("sets").item(0).getChildNodes(); + + palettes.clear(); + settypes.clear(); + + for(int i = 0; i < palettesList.getLength(); i++) { + Node nNode = palettesList.item(i); + Element element = (Element)nNode; + int paletteId = Integer.parseInt(element.getAttribute("id")); + FiguredataPalette palette = new FiguredataPalette(paletteId); + + NodeList colorsList = nNode.getChildNodes(); + for(int ii = 0; ii < colorsList.getLength(); ii++) { + Element colorElement = (Element)colorsList.item(ii); + FiguredataPaletteColor color = new FiguredataPaletteColor( + Integer.parseInt(colorElement.getAttribute("id")), + Integer.parseInt(colorElement.getAttribute("index")), + !colorElement.getAttribute("club").equals("0"), + colorElement.getAttribute("selectable").equals("1"), + colorElement.getTextContent() + ); + palette.addColor(color); + } + + palettes.put(palette.id, palette); + } + + for(int i = 0; i < settypesList.getLength(); i++) { + Node nNode = settypesList.item(i); + Element element = (Element)nNode; + + String type = element.getAttribute("type"); + int paletteId = Integer.parseInt(element.getAttribute("paletteid")); + boolean mandM0 = element.getAttribute("mand_m_0").equals("1"); + boolean mandF0 = element.getAttribute("mand_f_0").equals("1"); + boolean mandM1 = element.getAttribute("mand_m_1").equals("1"); + boolean mandF1 = element.getAttribute("mand_f_1").equals("1"); + + FiguredataSettype settype = new FiguredataSettype(type, paletteId, mandM0, mandF0, mandM1, mandF1); + + NodeList setsList = nNode.getChildNodes(); + for(int ii = 0; ii < setsList.getLength(); ii++) { + Element setElement = (Element)setsList.item(ii); + FiguredataSettypeSet set = new FiguredataSettypeSet( + Integer.parseInt(setElement.getAttribute("id")), + setElement.getAttribute("gender"), + !setElement.getAttribute("club").equals("0"), + setElement.getAttribute("colorable").equals("1"), + setElement.getAttribute("selectable").equals("1"), + setElement.getAttribute("preselectable").equals("1"), + setElement.getAttribute("sellable").equals("1") + ); + settype.addSet(set); + } + + settypes.put(settype.type, settype); + } + + } +} diff --git a/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataPalette.java b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataPalette.java new file mode 100644 index 00000000..b569d83f --- /dev/null +++ b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataPalette.java @@ -0,0 +1,35 @@ +package com.eu.habbo.habbohotel.users.clothingvalidation; + +import java.util.Map; +import java.util.TreeMap; + +public class FiguredataPalette { + public int id; + public Map colors; + + public FiguredataPalette(int id) { + this.id = id; + this.colors = new TreeMap<>(); + } + + public void addColor(FiguredataPaletteColor color) { + this.colors.put(color.id, color); + } + + public FiguredataPaletteColor getColor(int colorId) { + return this.colors.get(colorId); + } + + /** + * @return First non-club and selectable color + */ + public FiguredataPaletteColor getFirstNonHCColor() { + for(FiguredataPaletteColor color : this.colors.values()) { + if(!color.club && color.selectable) + return color; + } + + return this.colors.size() > 0 ? this.colors.entrySet().iterator().next().getValue() : null; + } + +} diff --git a/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataPaletteColor.java b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataPaletteColor.java new file mode 100644 index 00000000..7ba0b9a7 --- /dev/null +++ b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataPaletteColor.java @@ -0,0 +1,17 @@ +package com.eu.habbo.habbohotel.users.clothingvalidation; + +public class FiguredataPaletteColor { + public int id; + public int index; + public boolean club; + public boolean selectable; + public String colorHex; + + public FiguredataPaletteColor(int id, int index, boolean club, boolean selectable, String colorHex) { + this.id = id; + this.index = index; + this.club = club; + this.selectable = selectable; + this.colorHex = colorHex; + } +} diff --git a/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataSettype.java b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataSettype.java new file mode 100644 index 00000000..dfe6366d --- /dev/null +++ b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataSettype.java @@ -0,0 +1,60 @@ +package com.eu.habbo.habbohotel.users.clothingvalidation; + +import java.util.Map; +import java.util.TreeMap; + +public class FiguredataSettype { + public String type; + public int paletteId; + public boolean mandatoryMale0; + public boolean mandatoryFemale0; + public boolean mandatoryMale1; + public boolean mandatoryFemale1; + public Map sets; + + public FiguredataSettype(String type, int paletteId, boolean mandatoryMale0, boolean mandatoryFemale0, boolean mandatoryMale1, boolean mandatoryFemale1) { + this.type = type; + this.paletteId = paletteId; + this.mandatoryMale0 = mandatoryMale0; + this.mandatoryFemale0 = mandatoryFemale0; + this.mandatoryMale1 = mandatoryMale1; + this.mandatoryFemale1 = mandatoryFemale1; + this.sets = new TreeMap<>(); + } + + public void addSet(FiguredataSettypeSet set) { + this.sets.put(set.id, set); + } + + public FiguredataSettypeSet getSet(int id) { + return this.sets.get(id); + } + + /** + * @param gender Gender (M/F) + * @return First non-sellable and selectable set for given gender + */ + public FiguredataSettypeSet getFirstSetForGender(String gender) { + for(FiguredataSettypeSet set : this.sets.values()) { + if(set.gender.equalsIgnoreCase(gender) && !set.sellable && set.selectable) { + return set; + } + } + + return this.sets.size() > 0 ? this.sets.entrySet().iterator().next().getValue() : null; + } + + /** + * @param gender Gender (M/F) + * @return First non-club, non-sellable and selectable set for given gender + */ + public FiguredataSettypeSet getFirstNonHCSetForGender(String gender) { + for(FiguredataSettypeSet set : this.sets.values()) { + if(set.gender.equalsIgnoreCase(gender) && !set.club && !set.sellable && set.selectable) { + return set; + } + } + + return getFirstSetForGender(gender); + } +} diff --git a/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataSettypeSet.java b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataSettypeSet.java new file mode 100644 index 00000000..a5a15f38 --- /dev/null +++ b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataSettypeSet.java @@ -0,0 +1,21 @@ +package com.eu.habbo.habbohotel.users.clothingvalidation; + +public class FiguredataSettypeSet { + public int id; + public String gender; + public boolean club; + public boolean colorable; + public boolean selectable; + public boolean preselectable; + public boolean sellable; + + public FiguredataSettypeSet(int id, String gender, boolean club, boolean colorable, boolean selectable, boolean preselectable, boolean sellable) { + this.id = id; + this.gender = gender; + this.club = club; + this.colorable = colorable; + this.selectable = selectable; + this.preselectable = preselectable; + this.sellable = sellable; + } +} diff --git a/src/main/java/com/eu/habbo/habbohotel/users/inventory/WardrobeComponent.java b/src/main/java/com/eu/habbo/habbohotel/users/inventory/WardrobeComponent.java index 2d207132..cd806c24 100644 --- a/src/main/java/com/eu/habbo/habbohotel/users/inventory/WardrobeComponent.java +++ b/src/main/java/com/eu/habbo/habbohotel/users/inventory/WardrobeComponent.java @@ -14,15 +14,18 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.regex.Pattern; public class WardrobeComponent { private static final Logger LOGGER = LoggerFactory.getLogger(WardrobeComponent.class); private final THashMap looks; private final TIntSet clothing; + private final TIntSet clothingSets; public WardrobeComponent(Habbo habbo) { this.looks = new THashMap<>(); this.clothing = new TIntHashSet(); + this.clothingSets = new TIntHashSet(); try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) { try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM users_wardrobe WHERE user_id = ?")) { @@ -34,13 +37,19 @@ public class WardrobeComponent { } } - try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM users_clothing WHERE user_id = ?")) { + try (PreparedStatement statement = connection.prepareStatement("SELECT users_clothing.*, catalog_clothing.setid FROM users_clothing LEFT JOIN catalog_clothing ON catalog_clothing.id = users_clothing.clothing_id WHERE users_clothing.user_id = ?")) { statement.setInt(1, habbo.getHabboInfo().getId()); try (ResultSet set = statement.executeQuery()) { while (set.next()) { int value = set.getInt("clothing_id"); - this.clothing.add(value); + + for(String x : set.getString("setid").split(Pattern.quote(","))) { + try { + this.clothingSets.add(Integer.parseInt(x)); + } + catch (Exception e) { } + } } } } @@ -61,6 +70,10 @@ public class WardrobeComponent { return this.clothing; } + public TIntCollection getClothingSets() { + return this.clothingSets; + } + public void dispose() { this.looks.values().stream().filter(item -> item.needsInsert || item.needsUpdate).forEach(item -> { Emulator.getThreading().run(item); diff --git a/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/HcPayDayLogEntry.java b/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/HcPayDayLogEntry.java new file mode 100644 index 00000000..e96d64b3 --- /dev/null +++ b/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/HcPayDayLogEntry.java @@ -0,0 +1,64 @@ +package com.eu.habbo.habbohotel.users.subscriptions; + +import com.eu.habbo.Emulator; +import com.eu.habbo.core.DatabaseLoggable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * @author Beny + */ +public class HcPayDayLogEntry implements Runnable, DatabaseLoggable { + + private static final Logger LOGGER = LoggerFactory.getLogger(HcPayDayLogEntry.class); + private static final String QUERY = "INSERT INTO `logs_hc_payday` (`timestamp`, `user_id`, `hc_streak`, `total_coins_spent`, `reward_coins_spent`, `reward_streak`, `total_payout`, `currency`, `claimed`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + public final int timestamp; + public final int userId; + public final int hcStreak; + public final int totalCoinsSpent; + public final int rewardCoinsSpent; + public final int rewardStreak; + public final int totalPayout; + public final String currency; + public final boolean claimed; + + public HcPayDayLogEntry(int timestamp, int userId, int hcStreak, int totalCoinsSpent, int rewardCoinsSpent, int rewardStreak, int totalPayout, String currency, boolean claimed) { + this.timestamp = timestamp; + this.userId = userId; + this.hcStreak = hcStreak; + this.totalCoinsSpent = totalCoinsSpent; + this.rewardCoinsSpent = rewardCoinsSpent; + this.rewardStreak = rewardStreak; + this.totalPayout = totalPayout; + this.currency = currency; + this.claimed = claimed; + } + + @Override + public String getQuery() { + return QUERY; + } + + @Override + public void log(PreparedStatement statement) throws SQLException { + statement.setInt(1, this.timestamp); + statement.setInt(2, this.userId); + statement.setInt(3, this.hcStreak); + statement.setInt(4, this.totalCoinsSpent); + statement.setInt(5, this.rewardCoinsSpent); + statement.setInt(6, this.rewardStreak); + statement.setInt(7, this.totalPayout); + statement.setString(8, this.currency); + statement.setInt(9, this.claimed ? 1 : 0); + statement.addBatch(); + } + + @Override + public void run() { + Emulator.getDatabaseLogger().store(this); + } +} diff --git a/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/Subscription.java b/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/Subscription.java new file mode 100644 index 00000000..08fba035 --- /dev/null +++ b/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/Subscription.java @@ -0,0 +1,147 @@ +package com.eu.habbo.habbohotel.users.subscriptions; + +import com.eu.habbo.Emulator; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * @author Beny + */ +public class Subscription { + public static final String HABBO_CLUB = "HABBO_CLUB"; + + private final int id; + private final int userId; + private final String subscriptionType; + private final int timestampStart; + private int duration; + private boolean active; + + /** + * Subscription constructor + * @param id ID of the subscription + * @param userId ID of user who has the subscription + * @param subscriptionType Subscription type name (e.g. HABBO_CLUB) + * @param timestampStart Unix timestamp start of subscription + * @param duration Length of subscription in seconds + * @param active Boolean indicating if subscription is active + */ + public Subscription(Integer id, Integer userId, String subscriptionType, Integer timestampStart, Integer duration, Boolean active) { + this.id = id; + this.userId = userId; + this.subscriptionType = subscriptionType; + this.timestampStart = timestampStart; + this.duration = duration; + this.active = active; + } + + /** + * @return ID of the subscription + */ + public int getSubscriptionId() { + return id; + } + + /** + * @return ID of user who has the subscription + */ + public int getUserId() { + return userId; + } + + /** + * @return Subscription type name (e.g. HABBO_CLUB) + */ + public String getSubscriptionType() { + return subscriptionType; + } + + /** + * @return Length of subscription in seconds + */ + public int getDuration() { + return duration; + } + + /** + * Updates the Subscription record with new duration + * @param amount Length of time to add in seconds + */ + public void addDuration(int amount) { + this.duration += amount; + + try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) { + try (PreparedStatement statement = connection.prepareStatement("UPDATE `users_subscriptions` SET `duration` = ? WHERE `id` = ? LIMIT 1")) { + statement.setInt(1, this.duration); + statement.setInt(2, this.id); + statement.executeUpdate(); + } + } catch (SQLException e) { + SubscriptionManager.LOGGER.error("Caught SQL exception", e); + } + } + + /** + * Sets the subscription as active or inactive. If active and remaining time <= 0 the SubscriptionScheduler will inactivate the subscription and call onExpired() + * @param active Boolean indicating if the subscription is active + */ + public void setActive(boolean active) { + this.active = active; + + try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) { + try (PreparedStatement statement = connection.prepareStatement("UPDATE `users_subscriptions` SET `active` = ? WHERE `id` = ? LIMIT 1")) { + statement.setInt(1, this.active ? 1 : 0); + statement.setInt(2, this.id); + statement.executeUpdate(); + } + } catch (SQLException e) { + SubscriptionManager.LOGGER.error("Caught SQL exception", e); + } + } + + /** + * @return Remaining duration of subscription in seconds + */ + public int getRemaining() { + return (this.timestampStart + this.duration) - Emulator.getIntUnixTimestamp(); + } + + /** + * @return Unix timestamp start of subscription + */ + public int getTimestampStart() { + return this.timestampStart; + } + + /** + * @return Unix timestamp end of subscription + */ + public int getTimestampEnd() { + return (this.timestampStart + this.duration); + } + + /** + * @return Boolean indicating if the subscription is active + */ + public boolean isActive() { + return active; + } + + /** + * Called when the subscription is first created + */ + public void onCreated() { } + + /** + * Called when the subscription is extended or bought again when already exists + * @param duration Extended duration time in seconds + */ + public void onExtended(int duration) { } + + /** + * Called by SubscriptionScheduler when isActive() && getRemaining() < 0 + */ + public void onExpired() { } +} diff --git a/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/SubscriptionHabboClub.java b/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/SubscriptionHabboClub.java new file mode 100644 index 00000000..32e04120 --- /dev/null +++ b/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/SubscriptionHabboClub.java @@ -0,0 +1,371 @@ +package com.eu.habbo.habbohotel.users.subscriptions; + +import com.eu.habbo.Emulator; +import com.eu.habbo.database.Database; +import com.eu.habbo.habbohotel.messenger.Messenger; +import com.eu.habbo.habbohotel.rooms.RoomManager; +import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.habbohotel.users.HabboInfo; +import com.eu.habbo.habbohotel.users.clothingvalidation.ClothingValidationManager; +import com.eu.habbo.messages.outgoing.catalog.ClubCenterDataComposer; +import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDataComposer; +import com.eu.habbo.messages.outgoing.users.*; +import gnu.trove.map.hash.THashMap; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; +import java.util.TreeMap; + +/** + * @author Beny + */ +public class SubscriptionHabboClub extends Subscription { + + public static boolean HC_PAYDAY_ENABLED = false; + public static int HC_PAYDAY_NEXT_DATE = Integer.MAX_VALUE; // yyyy-MM-dd HH:mm:ss + public static String HC_PAYDAY_INTERVAL = ""; + public static String HC_PAYDAY_QUERY = ""; + public static TreeMap HC_PAYDAY_STREAK = new TreeMap<>(); + public static String HC_PAYDAY_CURRENCY = ""; + public static Double HC_PAYDAY_KICKBACK_PERCENTAGE = 0.1; + + /** + * When true "coins spent" will be calculated from the timestamp the user joins HC instead of from the last HC pay day execution timestamp + */ + public static boolean HC_PAYDAY_COINSSPENT_RESET_ON_EXPIRE = false; + + /** + * Boolean indicating if HC pay day currency executing. Prevents double execution + */ + public static boolean isExecuting = false; + + public SubscriptionHabboClub(Integer id, Integer userId, String subscriptionType, Integer timestampStart, Integer duration, Boolean active) { + super(id, userId, subscriptionType, timestampStart, duration, active); + } + + /** + * Called when the subscription is first created. + * Actions: + * - Set user's max_friends to MAXIMUM_FRIENDS_HC + * - Set user's max_rooms to MAXIMUM_ROOMS_HC + * - Reset the user's HC pay day timer (used in calculating the coins spent) + * - Send associated HC packets to client + */ + @Override + public void onCreated() { + super.onCreated(); + + Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(this.getUserId()); + habbo.getHabboStats().maxFriends = Messenger.MAXIMUM_FRIENDS_HC; + habbo.getHabboStats().maxRooms = RoomManager.MAXIMUM_ROOMS_HC; + habbo.getHabboStats().lastHCPayday = HC_PAYDAY_COINSSPENT_RESET_ON_EXPIRE ? Emulator.getIntUnixTimestamp() : HC_PAYDAY_NEXT_DATE - Emulator.timeStringToSeconds(HC_PAYDAY_INTERVAL); + Emulator.getThreading().run(habbo.getHabboStats()); + + if(habbo.getClient() != null) { + + if((Emulator.getIntUnixTimestamp() - habbo.getHabboStats().hcMessageLastModified) < 60) { + Emulator.getThreading().run(() -> { habbo.getClient().sendResponse(new UserClubComposer(habbo)); habbo.getClient().sendResponse(new UserPermissionsComposer(habbo)); }, (Emulator.getIntUnixTimestamp() - habbo.getHabboStats().hcMessageLastModified)); + } + else { + habbo.getClient().sendResponse(new UserClubComposer(habbo)); + habbo.getClient().sendResponse(new UserPermissionsComposer(habbo)); + } + } + } + + /** + * Called when the subscription is extended by manual action (by admin command or RCON) + * Actions: + * - Extend duration of the subscription + * - Send associated HC packets to client + */ + @Override + public void addDuration(int amount) { + super.addDuration(amount); + + if(amount < 0) { + Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(this.getUserId()); + if(habbo != null && habbo.getClient() != null) { + habbo.getClient().sendResponse(new UserClubComposer(habbo)); + habbo.getClient().sendResponse(new UserPermissionsComposer(habbo)); + } + } + } + + /** + * Called when the subscription is extended or bought again when already exists + * Actions: + * - Extend duration of the subscription + * - Send associated HC packets to client + */ + @Override + public void onExtended(int duration) { + super.onExtended(duration); + + Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(this.getUserId()); + + if(habbo.getClient() != null) { + habbo.getClient().sendResponse(new UserClubComposer(habbo)); + habbo.getClient().sendResponse(new UserPermissionsComposer(habbo)); + } + } + + /** + * Called by SubscriptionScheduler when isActive() && getRemaining() < 0 + * Actions: + * - Set user's max_friends to MAXIMUM_FRIENDS + * - Set user's max_rooms to MAXIMUM_ROOMS + * - Remove HC clothing + * - Send associated HC packets to client + */ + @Override + public void onExpired() { + super.onExpired(); + + Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(this.getUserId()); + habbo.getHabboStats().maxFriends = Messenger.MAXIMUM_FRIENDS; + habbo.getHabboStats().maxRooms = RoomManager.MAXIMUM_ROOMS_USER; + Emulator.getThreading().run(habbo.getHabboStats()); + + if(ClothingValidationManager.VALIDATE_ON_HC_EXPIRE) { + habbo.getHabboInfo().setLook(ClothingValidationManager.validateLook(habbo, habbo.getHabboInfo().getLook(), habbo.getHabboInfo().getGender().name())); + Emulator.getThreading().run(habbo.getHabboInfo()); + + if(habbo.getClient() != null) { + habbo.getClient().sendResponse(new UpdateUserLookComposer(habbo)); + } + + if (habbo.getHabboInfo().getCurrentRoom() != null) { + habbo.getHabboInfo().getCurrentRoom().sendComposer(new RoomUserDataComposer(habbo).compose()); + } + } + + if(habbo.getClient() != null) { + habbo.getClient().sendResponse(new UserClubComposer(habbo)); + habbo.getClient().sendResponse(new UserPermissionsComposer(habbo)); + } + } + + /** + * Calculate's a users upcoming HC Pay day rewards + * @param habbo User to calculate for + * @return ClubCenterDataComposer + */ + public static ClubCenterDataComposer calculatePayday(HabboInfo habbo) { + Subscription activeSub = null; + Subscription firstEverSub = null; + int currentHcStreak = 0; + int totalCreditsSpent = 0; + int creditRewardForStreakBonus = 0; + int creditRewardForMonthlySpent = 0; + int timeUntilPayday = 0; + + for(Subscription sub : habbo.getHabboStats().subscriptions) { + if(sub.getSubscriptionType().equalsIgnoreCase(Subscription.HABBO_CLUB)) { + + if(firstEverSub == null || sub.getTimestampStart() < firstEverSub.getTimestampStart()) { + firstEverSub = sub; + } + + if(sub.isActive()) { + activeSub = sub; + } + } + } + + if(HC_PAYDAY_ENABLED && activeSub != null) { + currentHcStreak = (int)Math.floor((Emulator.getIntUnixTimestamp() - activeSub.getTimestampStart()) / (60 * 60 * 24.0)); + if(currentHcStreak < 1) { + currentHcStreak = 0; + } + + for(Map.Entry set : HC_PAYDAY_STREAK.entrySet()) { + if(currentHcStreak >= set.getKey() && set.getValue() > creditRewardForStreakBonus) { + creditRewardForStreakBonus = set.getValue(); + } + } + + THashMap queryParams = new THashMap(); + queryParams.put("@user_id", habbo.getId()); + queryParams.put("@timestamp_start", habbo.getHabboStats().lastHCPayday); + queryParams.put("@timestamp_end", HC_PAYDAY_NEXT_DATE); + + try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); + PreparedStatement statement = Database.preparedStatementWithParams(connection, HC_PAYDAY_QUERY, queryParams)) { + + try (ResultSet set = statement.executeQuery()) { + while (set.next()) { + totalCreditsSpent = set.getInt("amount_spent"); + } + } + + } catch (SQLException e) { + SubscriptionManager.LOGGER.error("Caught SQL exception", e); + } + + creditRewardForMonthlySpent = (int)Math.floor(totalCreditsSpent * HC_PAYDAY_KICKBACK_PERCENTAGE); + + timeUntilPayday = (HC_PAYDAY_NEXT_DATE - Emulator.getIntUnixTimestamp()) / 60; + } + + return new ClubCenterDataComposer( + currentHcStreak, + (firstEverSub != null ? new SimpleDateFormat("dd-MM-yyyy").format(new Date(firstEverSub.getTimestampStart() * 1000L)) : ""), + HC_PAYDAY_KICKBACK_PERCENTAGE, + 0, + 0, + totalCreditsSpent, + creditRewardForStreakBonus, + creditRewardForMonthlySpent, + timeUntilPayday + ); + } + + /** + * Executes the HC Pay day, calculating reward for all active HABBO_CLUB subscribers and issuing rewards. + */ + public static void executePayDay() { + isExecuting = true; + int timestampNow = Emulator.getIntUnixTimestamp(); + + try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); + PreparedStatement statement = connection.prepareStatement("SELECT user_id FROM `users_subscriptions` WHERE subscription_type = '" + Subscription.HABBO_CLUB + "' AND `active` = 1 AND `timestamp_start` < ? AND (`timestamp_start` + `duration`) > ? GROUP BY user_id")) { + statement.setInt(1, timestampNow); + statement.setInt(2, timestampNow); + + try (ResultSet set = statement.executeQuery()) { + while (set.next()) { + try { + int userId = set.getInt("user_id"); + HabboInfo habboInfo = Emulator.getGameEnvironment().getHabboManager().getHabboInfo(userId); + ClubCenterDataComposer calculated = calculatePayday(habboInfo); + int totalReward = (calculated.creditRewardForMonthlySpent + calculated.creditRewardForStreakBonus); + if(totalReward > 0) { + boolean claimed = claimPayDay(Emulator.getGameEnvironment().getHabboManager().getHabbo(userId), totalReward, HC_PAYDAY_CURRENCY); + HcPayDayLogEntry le = new HcPayDayLogEntry(timestampNow, userId, calculated.currentHcStreak, calculated.totalCreditsSpent, calculated.creditRewardForMonthlySpent, calculated.creditRewardForStreakBonus, totalReward, HC_PAYDAY_CURRENCY, claimed); + Emulator.getThreading().run(le); + } + habboInfo.getHabboStats().lastHCPayday = timestampNow; + } + catch (Exception e) { + SubscriptionManager.LOGGER.error("Exception processing HC payday for user #" + set.getInt("user_id"), e); + } + } + } + + Date date = new java.util.Date(HC_PAYDAY_NEXT_DATE * 1000L); + date = Emulator.modifyDate(date, HC_PAYDAY_INTERVAL); + HC_PAYDAY_NEXT_DATE = (int)(date.getTime() / 1000L); + + try(PreparedStatement stm2 = connection.prepareStatement("UPDATE `emulator_settings` SET `value` = ? WHERE `key` = ?")) { + SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + stm2.setString(1, sdf.format(date)); + stm2.setString(2, "subscriptions.hc.payday.next_date"); + stm2.execute(); + } + + try(PreparedStatement stm2 = connection.prepareStatement("UPDATE users_settings SET last_hc_payday = ? WHERE user_id IN (SELECT user_id FROM `users_subscriptions` WHERE subscription_type = '" + Subscription.HABBO_CLUB + "' AND `active` = 1 AND `timestamp_start` < ? AND (`timestamp_start` + `duration`) > ? GROUP BY user_id)")) { + stm2.setInt(1, timestampNow); + stm2.setInt(2, timestampNow); + stm2.setInt(3, timestampNow); + stm2.execute(); + } + + } + catch (SQLException e) { + SubscriptionManager.LOGGER.error("Caught SQL exception", e); + } + isExecuting = false; + } + + /** + * Called when a user logs in. Checks for any unclaimed HC Pay day rewards and issues rewards. + * @param habbo User to process + */ + public static void processUnclaimed(Habbo habbo) { + try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); + PreparedStatement statement = connection.prepareStatement("SELECT * FROM `logs_hc_payday` WHERE user_id = ? AND claimed = 0")) { + statement.setInt(1, habbo.getHabboInfo().getId()); + + try (ResultSet set = statement.executeQuery()) { + while (set.next()) { + try { + int logId = set.getInt("id"); + int userId = set.getInt("user_id"); + int totalPayout = set.getInt("total_payout"); + String currency = set.getString("currency"); + + if(claimPayDay(habbo, totalPayout, currency)) { + try(PreparedStatement stm2 = connection.prepareStatement("UPDATE logs_hc_payday SET claimed = 1 WHERE id = ?")) { + stm2.setInt(1, logId); + stm2.execute(); + } + } + } + catch (Exception e) { + SubscriptionManager.LOGGER.error("Exception processing HC payday for user #" + set.getInt("user_id"), e); + } + } + } + + } + catch (SQLException e) { + SubscriptionManager.LOGGER.error("Caught SQL exception", e); + } + } + + /** + * Issues rewards to user. + * @param habbo User to reward to + * @param amount Amount of currency to reward + * @param currency Currency string (Can be one of: credits, diamonds, duckets, pixels or a currency ID e.g. 5) + * @return Boolean indicating success of the operation + */ + public static boolean claimPayDay(Habbo habbo, int amount, String currency) { + if(habbo == null) + return false; + + int pointCurrency; + switch(currency.toLowerCase()) { + case "credits": + case "coins": + case "credit": + case "coin": + habbo.getClient().getHabbo().getHabboInfo().addCredits(amount); + habbo.getClient().sendResponse(new UserCreditsComposer(habbo.getClient().getHabbo())); + break; + + case "diamonds": + case "diamond": + pointCurrency = 5; + habbo.getClient().getHabbo().getHabboInfo().addCurrencyAmount(pointCurrency, amount); + habbo.getClient().sendResponse(new UserPointsComposer(habbo.getClient().getHabbo().getHabboInfo().getCurrencyAmount(pointCurrency), amount, pointCurrency)); + break; + + case "duckets": + case "ducket": + case "pixels": + case "pixel": + pointCurrency = 0; + habbo.getClient().getHabbo().getHabboInfo().addCurrencyAmount(pointCurrency, amount); + habbo.getClient().sendResponse(new UserPointsComposer(habbo.getClient().getHabbo().getHabboInfo().getCurrencyAmount(pointCurrency), amount, pointCurrency)); + break; + + default: + pointCurrency = Integer.parseInt(currency); + habbo.getClient().getHabbo().getHabboInfo().addCurrencyAmount(pointCurrency, amount); + habbo.getClient().sendResponse(new UserPointsComposer(habbo.getClient().getHabbo().getHabboInfo().getCurrencyAmount(pointCurrency), amount, pointCurrency)); + break; + } + + habbo.alert(Emulator.getTexts().getValue("subscriptions.hc.payday.message", "Woohoo HC Payday has arrived! You have received %amount% credits to your purse. Enjoy!").replace("%amount%", "" + amount)); + + return true; + } + +} diff --git a/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/SubscriptionManager.java b/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/SubscriptionManager.java new file mode 100644 index 00000000..b39d9d52 --- /dev/null +++ b/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/SubscriptionManager.java @@ -0,0 +1,89 @@ +package com.eu.habbo.habbohotel.users.subscriptions; + +import com.eu.habbo.Emulator; +import gnu.trove.map.hash.THashMap; +import gnu.trove.set.hash.THashSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * @author Beny + */ +public class SubscriptionManager { + + public static final Logger LOGGER = LoggerFactory.getLogger(SubscriptionManager.class); + + public THashMap> types; + + public SubscriptionManager() { + this.types = new THashMap<>(); + } + + public void init() { + this.types.put(Subscription.HABBO_CLUB, SubscriptionHabboClub.class); + } + + public void addSubscriptionType(String type, Class clazz) { + if(this.types.containsKey(type) || this.types.containsValue(clazz)) { + throw new RuntimeException("Subscription Type must be unique. An class with type: " + clazz.getName() + " was already added OR the key: " + type + " is already in use."); + } + + this.types.put(type, clazz); + } + + public void removeSubscriptionType(String type) { + this.types.remove(type); + } + + public Class getSubscriptionClass(String type) { + if(!this.types.containsKey(type)) { + LOGGER.debug("Can't find subscription class: {}", type); + return Subscription.class; + } + + return this.types.get(type); + } + + public void dispose() { + this.types.clear(); + } + + public THashSet getSubscriptionsForUser(int userId) { + THashSet subscriptions = new THashSet<>(); + + try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); + PreparedStatement statement = connection.prepareStatement("SELECT * FROM users_subscriptions WHERE user_id = ?")) { + statement.setInt(1, userId); + + try (ResultSet set = statement.executeQuery()) { + while (set.next()) { + Class subClazz = Emulator.getGameEnvironment().getSubscriptionManager().getSubscriptionClass(set.getString("subscription_type")); + Constructor c = subClazz.getConstructor(Integer.class, Integer.class, String.class, Integer.class, Integer.class, Boolean.class); + c.setAccessible(true); + Subscription subscription = c.newInstance(set.getInt("id"), set.getInt("user_id"), set.getString("subscription_type"), set.getInt("timestamp_start"), set.getInt("duration"), set.getInt("active") == 1); + subscriptions.add(subscription); + } + } catch (IllegalAccessException e) { + LOGGER.error("IllegalAccessException", e); + } catch (InstantiationException e) { + LOGGER.error("InstantiationException", e); + } catch (InvocationTargetException e) { + LOGGER.error("InvocationTargetException", e); + } + } catch (SQLException e) { + LOGGER.error("Caught SQL exception", e); + } + catch (NoSuchMethodException e) { + LOGGER.error("Caught NoSuchMethodException", e); + } + + return subscriptions; + } +} diff --git a/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/SubscriptionScheduler.java b/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/SubscriptionScheduler.java new file mode 100644 index 00000000..9f18a449 --- /dev/null +++ b/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/SubscriptionScheduler.java @@ -0,0 +1,74 @@ +package com.eu.habbo.habbohotel.users.subscriptions; + +import com.eu.habbo.Emulator; +import com.eu.habbo.core.Scheduler; +import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.plugin.events.users.subscriptions.UserSubscriptionExpiredEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +/** + * @author Beny + */ +public class SubscriptionScheduler extends Scheduler { + + private static final Logger LOGGER = LoggerFactory.getLogger(SubscriptionScheduler.class); + + public SubscriptionScheduler() { + super(Emulator.getConfig().getInt("subscriptions.scheduler.interval", 10)); + this.reloadConfig(); + } + + /** + * Called when config is changed. Should end the scheduler if disabled. + */ + public void reloadConfig() { + if (Emulator.getConfig().getBoolean("subscriptions.scheduler.enabled", true)) { + if (this.disposed) { + this.disposed = false; + this.run(); + } + } else { + this.disposed = true; + } + } + + @Override + public void run() { + super.run(); + + Habbo habbo; + for (Map.Entry map : Emulator.getGameEnvironment().getHabboManager().getOnlineHabbos().entrySet()) { + habbo = map.getValue(); + + try { + if (habbo != null) { + for(Subscription subscription : habbo.getHabboStats().subscriptions) { + if(subscription.isActive() && subscription.getRemaining() < 0) { + if (!Emulator.getPluginManager().fireEvent(new UserSubscriptionExpiredEvent(habbo.getHabboInfo().getId(), subscription)).isCancelled()) { + subscription.onExpired(); + subscription.setActive(false); + } + } + } + } + } catch (Exception e) { + LOGGER.error("Caught exception", e); + } + } + + if(SubscriptionHabboClub.HC_PAYDAY_ENABLED && !SubscriptionHabboClub.isExecuting && SubscriptionHabboClub.HC_PAYDAY_NEXT_DATE < Emulator.getIntUnixTimestamp()) { + SubscriptionHabboClub.executePayDay(); + } + } + + public boolean isDisposed() { + return this.disposed; + } + + public void setDisposed(boolean disposed) { + this.disposed = disposed; + } +} diff --git a/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogBuyItemEvent.java b/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogBuyItemEvent.java index 5e2fe085..17915caf 100644 --- a/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogBuyItemEvent.java +++ b/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogBuyItemEvent.java @@ -12,6 +12,7 @@ import com.eu.habbo.habbohotel.permissions.Permission; import com.eu.habbo.habbohotel.pets.PetManager; import com.eu.habbo.habbohotel.users.HabboBadge; import com.eu.habbo.habbohotel.users.HabboInventory; +import com.eu.habbo.habbohotel.users.subscriptions.Subscription; import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.outgoing.catalog.AlertPurchaseFailedComposer; import com.eu.habbo.messages.outgoing.catalog.AlertPurchaseUnavailableComposer; @@ -176,12 +177,19 @@ public class CatalogBuyItemEvent extends MessageHandler { if (!this.client.getHabbo().hasPermission(Permission.ACC_INFINITE_POINTS)) this.client.getHabbo().getHabboInfo().addCurrencyAmount(item.getPointsType(), -totalDuckets); - if (this.client.getHabbo().getHabboStats().getClubExpireTimestamp() <= Emulator.getIntUnixTimestamp()) + + if(this.client.getHabbo().getHabboStats().createSubscription(Subscription.HABBO_CLUB, (totalDays * 86400)) == null) { + this.client.sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR).compose()); + throw new Exception("Unable to create or extend subscription"); + } + + /*if (this.client.getHabbo().getHabboStats().getClubExpireTimestamp() <= Emulator.getIntUnixTimestamp()) this.client.getHabbo().getHabboStats().setClubExpireTimestamp(Emulator.getIntUnixTimestamp()); this.client.getHabbo().getHabboStats().setClubExpireTimestamp(this.client.getHabbo().getHabboStats().getClubExpireTimestamp() + (totalDays * 86400)); + this.client.sendResponse(new UserPermissionsComposer(this.client.getHabbo())); - this.client.sendResponse(new UserClubComposer(this.client.getHabbo())); + this.client.sendResponse(new UserClubComposer(this.client.getHabbo()));*/ if (totalCredits > 0) this.client.sendResponse(new UserCreditsComposer(this.client.getHabbo())); diff --git a/src/main/java/com/eu/habbo/messages/incoming/catalog/RequestClubDataEvent.java b/src/main/java/com/eu/habbo/messages/incoming/catalog/RequestClubDataEvent.java index 259b5799..79eb7525 100644 --- a/src/main/java/com/eu/habbo/messages/incoming/catalog/RequestClubDataEvent.java +++ b/src/main/java/com/eu/habbo/messages/incoming/catalog/RequestClubDataEvent.java @@ -1,11 +1,14 @@ package com.eu.habbo.messages.incoming.catalog; +import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionHabboClub; import com.eu.habbo.messages.incoming.MessageHandler; +import com.eu.habbo.messages.outgoing.catalog.ClubCenterDataComposer; import com.eu.habbo.messages.outgoing.catalog.ClubDataComposer; public class RequestClubDataEvent extends MessageHandler { @Override public void handle() throws Exception { this.client.sendResponse(new ClubDataComposer(this.client.getHabbo(), this.packet.readInt())); + this.client.sendResponse(SubscriptionHabboClub.calculatePayday(this.client.getHabbo().getHabboInfo())); } } diff --git a/src/main/java/com/eu/habbo/messages/incoming/friends/AcceptFriendRequestEvent.java b/src/main/java/com/eu/habbo/messages/incoming/friends/AcceptFriendRequestEvent.java index aa5b1821..d5197131 100644 --- a/src/main/java/com/eu/habbo/messages/incoming/friends/AcceptFriendRequestEvent.java +++ b/src/main/java/com/eu/habbo/messages/incoming/friends/AcceptFriendRequestEvent.java @@ -4,6 +4,8 @@ import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.messenger.Messenger; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.messages.incoming.MessageHandler; +import com.eu.habbo.messages.outgoing.friends.FriendRequestErrorComposer; +import com.eu.habbo.plugin.PluginManager; public class AcceptFriendRequestEvent extends MessageHandler { @Override @@ -17,18 +19,33 @@ public class AcceptFriendRequestEvent extends MessageHandler { if (userId == 0) return; - if (this.client.getHabbo().getMessenger().getFriends().containsKey(userId)) + if (this.client.getHabbo().getMessenger().getFriends().containsKey(userId)) { + this.client.getHabbo().getMessenger().deleteFriendRequests(userId, this.client.getHabbo().getHabboInfo().getId()); continue; + } + + Habbo target = Emulator.getGameEnvironment().getHabboManager().getHabbo(userId); + + if(target == null) { + this.client.sendResponse(new FriendRequestErrorComposer(FriendRequestErrorComposer.TARGET_NOT_FOUND)); + this.client.getHabbo().getMessenger().deleteFriendRequests(userId, this.client.getHabbo().getHabboInfo().getId()); + continue; + } + + if(this.client.getHabbo().getMessenger().getFriends().size() >= this.client.getHabbo().getHabboStats().maxFriends && !this.client.getHabbo().hasPermission("acc_infinite_friends")) { + this.client.sendResponse(new FriendRequestErrorComposer(FriendRequestErrorComposer.FRIEND_LIST_OWN_FULL)); + break; + } + + if(target.getMessenger().getFriends().size() >= target.getHabboStats().maxFriends && !target.hasPermission("acc_infinite_friends")) { + this.client.sendResponse(new FriendRequestErrorComposer(FriendRequestErrorComposer.FRIEND_LIST_TARGET_FULL)); + continue; + } this.client.getHabbo().getMessenger().acceptFriendRequest(userId, this.client.getHabbo().getHabboInfo().getId()); Messenger.checkFriendSizeProgress(this.client.getHabbo()); - - Habbo target = Emulator.getGameEnvironment().getHabboManager().getHabbo(userId); - - if (target != null) { - Messenger.checkFriendSizeProgress(target); - } + Messenger.checkFriendSizeProgress(target); } } } diff --git a/src/main/java/com/eu/habbo/messages/incoming/friends/FriendRequestEvent.java b/src/main/java/com/eu/habbo/messages/incoming/friends/FriendRequestEvent.java index f595d730..758ca1b4 100644 --- a/src/main/java/com/eu/habbo/messages/incoming/friends/FriendRequestEvent.java +++ b/src/main/java/com/eu/habbo/messages/incoming/friends/FriendRequestEvent.java @@ -74,10 +74,16 @@ public class FriendRequestEvent extends MessageHandler { return; } - if (this.client.getHabbo().getMessenger().getFriends().values().size() >= Messenger.friendLimit(this.client.getHabbo()) && !this.client.getHabbo().hasPermission("acc_infinite_friends")) { + if (this.client.getHabbo().getMessenger().getFriends().values().size() >= this.client.getHabbo().getHabboStats().maxFriends && !this.client.getHabbo().hasPermission("acc_infinite_friends")) { this.client.sendResponse(new FriendRequestErrorComposer(FriendRequestErrorComposer.FRIEND_LIST_OWN_FULL)); return; } + + if (habbo.getMessenger().getFriends().values().size() >= habbo.getHabboStats().maxFriends && !habbo.hasPermission("acc_infinite_friends")) { + this.client.sendResponse(new FriendRequestErrorComposer(FriendRequestErrorComposer.FRIEND_LIST_TARGET_FULL)); + return; + } + Messenger.makeFriendRequest(this.client.getHabbo().getHabboInfo().getId(), id); } else { this.client.sendResponse(new FriendRequestErrorComposer(FriendRequestErrorComposer.TARGET_NOT_FOUND)); diff --git a/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java b/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java index afe9ddf5..f28c9758 100644 --- a/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java +++ b/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java @@ -8,6 +8,8 @@ import com.eu.habbo.habbohotel.navigation.NavigatorSavedSearch; import com.eu.habbo.habbohotel.permissions.Permission; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboManager; +import com.eu.habbo.habbohotel.users.clothingvalidation.ClothingValidationManager; +import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionHabboClub; import com.eu.habbo.messages.NoAuthMessage; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.incoming.MessageHandler; @@ -105,6 +107,14 @@ public class SecureLoginEvent extends MessageHandler { Emulator.getGameServer().getGameClientManager().disposeClient(this.client); return; } + + if(ClothingValidationManager.VALIDATE_ON_LOGIN) { + String validated = ClothingValidationManager.validateLook(this.client.getHabbo()); + if(!validated.equals(this.client.getHabbo().getHabboInfo().getLook())) { + this.client.getHabbo().getHabboInfo().setLook(validated); + } + } + ArrayList messages = new ArrayList<>(); messages.add(new SecureLoginOKComposer().compose()); @@ -194,6 +204,10 @@ public class SecureLoginEvent extends MessageHandler { }, Emulator.getConfig().getInt("hotel.welcome.alert.delay", 5000)); } + if(SubscriptionHabboClub.HC_PAYDAY_ENABLED) { + SubscriptionHabboClub.processUnclaimed(habbo); + } + Messenger.checkFriendSizeProgress(habbo); if (!habbo.getHabboStats().hasGottenDefaultSavedSearches) { diff --git a/src/main/java/com/eu/habbo/messages/incoming/navigator/RequestCanCreateRoomEvent.java b/src/main/java/com/eu/habbo/messages/incoming/navigator/RequestCanCreateRoomEvent.java index ef0b56df..d1ed2bf0 100644 --- a/src/main/java/com/eu/habbo/messages/incoming/navigator/RequestCanCreateRoomEvent.java +++ b/src/main/java/com/eu/habbo/messages/incoming/navigator/RequestCanCreateRoomEvent.java @@ -9,7 +9,7 @@ public class RequestCanCreateRoomEvent extends MessageHandler { @Override public void handle() throws Exception { int count = Emulator.getGameEnvironment().getRoomManager().getRoomsForHabbo(this.client.getHabbo()).size(); - int max = this.client.getHabbo().getHabboStats().hasActiveClub() ? RoomManager.MAXIMUM_ROOMS_VIP : RoomManager.MAXIMUM_ROOMS_USER; + int max = this.client.getHabbo().getHabboStats().maxRooms; this.client.sendResponse(new CanCreateRoomComposer(count, max)); } } diff --git a/src/main/java/com/eu/habbo/messages/incoming/navigator/RequestCreateRoomEvent.java b/src/main/java/com/eu/habbo/messages/incoming/navigator/RequestCreateRoomEvent.java index 49328abf..d0f8f83a 100644 --- a/src/main/java/com/eu/habbo/messages/incoming/navigator/RequestCreateRoomEvent.java +++ b/src/main/java/com/eu/habbo/messages/incoming/navigator/RequestCreateRoomEvent.java @@ -48,7 +48,7 @@ public class RequestCreateRoomEvent extends MessageHandler { return; int count = Emulator.getGameEnvironment().getRoomManager().getRoomsForHabbo(this.client.getHabbo()).size(); - int max = this.client.getHabbo().getHabboStats().hasActiveClub() ? RoomManager.MAXIMUM_ROOMS_VIP : RoomManager.MAXIMUM_ROOMS_USER; + int max = this.client.getHabbo().getHabboStats().maxRooms; if (count >= max) { this.client.sendResponse(new CanCreateRoomComposer(count, max)); diff --git a/src/main/java/com/eu/habbo/messages/incoming/rooms/items/RedeemClothingEvent.java b/src/main/java/com/eu/habbo/messages/incoming/rooms/items/RedeemClothingEvent.java index 95382469..20f46659 100644 --- a/src/main/java/com/eu/habbo/messages/incoming/rooms/items/RedeemClothingEvent.java +++ b/src/main/java/com/eu/habbo/messages/incoming/rooms/items/RedeemClothingEvent.java @@ -53,6 +53,7 @@ public class RedeemClothingEvent extends MessageHandler { } 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())); this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FIGURESET_REDEEMED.key)); diff --git a/src/main/java/com/eu/habbo/messages/incoming/users/UserSaveLookEvent.java b/src/main/java/com/eu/habbo/messages/incoming/users/UserSaveLookEvent.java index 411f24e3..c4c30c5f 100644 --- a/src/main/java/com/eu/habbo/messages/incoming/users/UserSaveLookEvent.java +++ b/src/main/java/com/eu/habbo/messages/incoming/users/UserSaveLookEvent.java @@ -4,6 +4,7 @@ import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.achievements.AchievementManager; import com.eu.habbo.habbohotel.modtool.ScripterManager; import com.eu.habbo.habbohotel.users.HabboGender; +import com.eu.habbo.habbohotel.users.clothingvalidation.ClothingValidationManager; import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDataComposer; import com.eu.habbo.messages.outgoing.users.UpdateUserLookComposer; @@ -35,7 +36,7 @@ public class UserSaveLookEvent extends MessageHandler { if (lookEvent.isCancelled()) return; - this.client.getHabbo().getHabboInfo().setLook(lookEvent.newLook); + this.client.getHabbo().getHabboInfo().setLook(ClothingValidationManager.VALIDATE_ON_CHANGE_LOOKS ? ClothingValidationManager.validateLook(this.client.getHabbo(), lookEvent.newLook, lookEvent.gender.name()) : lookEvent.newLook); this.client.getHabbo().getHabboInfo().setGender(lookEvent.gender); Emulator.getThreading().run(this.client.getHabbo().getHabboInfo()); this.client.sendResponse(new UpdateUserLookComposer(this.client.getHabbo())); diff --git a/src/main/java/com/eu/habbo/messages/outgoing/catalog/ClubCenterDataComposer.java b/src/main/java/com/eu/habbo/messages/outgoing/catalog/ClubCenterDataComposer.java index 28b1472c..64e4a3f0 100644 --- a/src/main/java/com/eu/habbo/messages/outgoing/catalog/ClubCenterDataComposer.java +++ b/src/main/java/com/eu/habbo/messages/outgoing/catalog/ClubCenterDataComposer.java @@ -1,40 +1,54 @@ package com.eu.habbo.messages.outgoing.catalog; +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.habbohotel.users.subscriptions.Subscription; +import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionHabboClub; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.outgoing.MessageComposer; import com.eu.habbo.messages.outgoing.Outgoing; -public class ClubCenterDataComposer extends MessageComposer { - private final int streakDuration; - private final String joinDate; - private final double percentage; - private final int creditsSpend; - private final int creditsBonus; - private final int spendBonus; - private final int delay; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; - public ClubCenterDataComposer(int streakDuration, String joinDate, double percentage, int creditsSpend, int creditsBonus, int spendBonus, int delay) { - this.streakDuration = streakDuration; - this.joinDate = joinDate; - this.percentage = percentage; - this.creditsSpend = creditsSpend; - this.creditsBonus = creditsBonus; - this.spendBonus = spendBonus; - this.delay = delay; +public class ClubCenterDataComposer extends MessageComposer { + public final int currentHcStreak; + public final String firstSubDate; + public final double kickbackPercentage; + public final int totalCreditsMissed; + public final int totalCreditsRewarded; + public final int totalCreditsSpent; + public final int creditRewardForStreakBonus; + public final int creditRewardForMonthlySpent; + public final int timeUntilPayday; + + public ClubCenterDataComposer(int currentHcStreak, String firstSubDate, double kickbackPercentage, int totalCreditsMissed, int totalCreditsRewarded, int totalCreditsSpent, int creditRewardForStreakBonus, int creditRewardForMonthlySpent, int timeUntilPayday) { + this.currentHcStreak = currentHcStreak; + this.firstSubDate = firstSubDate; + this.kickbackPercentage = kickbackPercentage; + this.totalCreditsMissed = totalCreditsMissed; + this.totalCreditsRewarded = totalCreditsRewarded; + this.totalCreditsSpent = totalCreditsSpent; + this.creditRewardForStreakBonus = creditRewardForStreakBonus; + this.creditRewardForMonthlySpent = creditRewardForMonthlySpent; + this.timeUntilPayday = timeUntilPayday; } @Override protected ServerMessage composeInternal() { this.response.init(Outgoing.ClubCenterDataComposer); - this.response.appendInt(this.streakDuration); //streakduration in days - this.response.appendString(this.joinDate); //joindate - this.response.appendDouble(this.percentage); //percentage - this.response.appendInt(0); //Unused - this.response.appendInt(0); //unused - this.response.appendInt(this.creditsSpend); //Amount credits spend - this.response.appendInt(this.creditsBonus); //Credits bonus - this.response.appendInt(this.spendBonus); //Spend bonus - this.response.appendInt(this.delay); //next pay in minutes + this.response.appendInt(this.currentHcStreak); // currentHcStreak (days) + this.response.appendString(this.firstSubDate); // firstSubscriptionDate (dd-mm-yyyy) + this.response.appendDouble(this.kickbackPercentage); // kickbackPercentage (e.g. 0.1 for 10%) + this.response.appendInt(this.totalCreditsMissed); // (not used) + this.response.appendInt(this.totalCreditsRewarded); // (not used) + this.response.appendInt(this.totalCreditsSpent); + this.response.appendInt(this.creditRewardForStreakBonus); + this.response.appendInt(this.creditRewardForMonthlySpent); + this.response.appendInt(this.timeUntilPayday); // timeUntilPayday (minutes) return this.response; } + + } \ No newline at end of file diff --git a/src/main/java/com/eu/habbo/messages/outgoing/friends/FriendRequestErrorComposer.java b/src/main/java/com/eu/habbo/messages/outgoing/friends/FriendRequestErrorComposer.java index 751dbc77..84ebe13b 100644 --- a/src/main/java/com/eu/habbo/messages/outgoing/friends/FriendRequestErrorComposer.java +++ b/src/main/java/com/eu/habbo/messages/outgoing/friends/FriendRequestErrorComposer.java @@ -6,6 +6,7 @@ import com.eu.habbo.messages.outgoing.Outgoing; public class FriendRequestErrorComposer extends MessageComposer { public static final int FRIEND_LIST_OWN_FULL = 1; + public static final int FRIEND_LIST_TARGET_FULL = 2; public static final int TARGET_NOT_ACCEPTING_REQUESTS = 3; public static final int TARGET_NOT_FOUND = 4; diff --git a/src/main/java/com/eu/habbo/messages/outgoing/friends/FriendsComposer.java b/src/main/java/com/eu/habbo/messages/outgoing/friends/FriendsComposer.java index 0253f33f..2bd854e2 100644 --- a/src/main/java/com/eu/habbo/messages/outgoing/friends/FriendsComposer.java +++ b/src/main/java/com/eu/habbo/messages/outgoing/friends/FriendsComposer.java @@ -30,7 +30,7 @@ public class FriendsComposer extends MessageComposer { //this.response.appendInt(300); //this.response.appendInt(3); //Club level this.response.appendInt(this.habbo.hasPermission("acc_infinite_friends") ? Integer.MAX_VALUE : Messenger.MAXIMUM_FRIENDS); - this.response.appendInt(this.habbo.hasPermission("acc_infinite_friends") ? Integer.MAX_VALUE : Messenger.MAXIMUM_FRIENDS); + this.response.appendInt(this.habbo.hasPermission("acc_infinite_friends") ? Integer.MAX_VALUE : Messenger.MAXIMUM_FRIENDS_HC); this.response.appendInt(this.habbo.getMessenger().getFriends().size()/* + (this.habbo.hasPermission("acc_staff_chat") ? 1 : 0)*/); for (Map.Entry row : this.habbo.getMessenger().getFriends().entrySet()) { diff --git a/src/main/java/com/eu/habbo/messages/outgoing/users/UserClubComposer.java b/src/main/java/com/eu/habbo/messages/outgoing/users/UserClubComposer.java index 6e5f60bc..6bcb5f65 100644 --- a/src/main/java/com/eu/habbo/messages/outgoing/users/UserClubComposer.java +++ b/src/main/java/com/eu/habbo/messages/outgoing/users/UserClubComposer.java @@ -2,10 +2,15 @@ package com.eu.habbo.messages.outgoing.users; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.habbohotel.users.subscriptions.Subscription; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.outgoing.MessageComposer; import com.eu.habbo.messages.outgoing.Outgoing; +import java.time.Period; +import java.util.Date; +import java.util.concurrent.TimeUnit; + public class UserClubComposer extends MessageComposer { private final Habbo habbo; @@ -19,6 +24,47 @@ public class UserClubComposer extends MessageComposer { this.response.appendString("club_habbo"); + Subscription subscription = this.habbo.getHabboStats().getSubscription(Subscription.HABBO_CLUB); + + int days = 0; + int minutes = 0; + int timeRemaining = 0; + + if(subscription != null) { + timeRemaining = subscription.getRemaining(); + days = (int) Math.floor(timeRemaining / 86400.0); + minutes = (int) Math.ceil(timeRemaining / 60.0); + + if(days < 1 && minutes > 0) { + days = 1; + } + } + + this.response.appendInt(days); // daysToPeriodEnd + this.response.appendInt(0); // memberPeriods + this.response.appendInt(0); // periodsSubscribedAhead + this.response.appendInt(0); // responseType + this.response.appendBoolean(true); // hasEverBeenMember + this.response.appendBoolean(true); // isVIP + this.response.appendInt(0); // pastClubDays + this.response.appendInt(0); // pastVIPdays + this.response.appendInt(minutes); // minutesTillExpiration + this.response.appendInt((Emulator.getIntUnixTimestamp() - this.habbo.getHabboStats().hcMessageLastModified) / 60); // minutesSinceLastModified + this.habbo.getHabboStats().hcMessageLastModified = Emulator.getIntUnixTimestamp(); + + // int - daysToPeriodEnd + // int - memberPeriods + // int - periodsSubscribedAhead + // int - responseType + // bool - hasEverBeenMember + // bool - isVIP + // int - pastClubDays + // int - pastVIPdays + // int - minutesTillExpiration + // (optional) int - minutesSinceLastModified + + + /* int endTimestamp = this.habbo.getHabboStats().getClubExpireTimestamp(); int now = Emulator.getIntUnixTimestamp(); @@ -48,6 +94,7 @@ public class UserClubComposer extends MessageComposer { this.response.appendInt(0); this.response.appendInt(1); } + this.response.appendBoolean(true); this.response.appendBoolean(true); this.response.appendInt(0); @@ -60,6 +107,7 @@ public class UserClubComposer extends MessageComposer { } else { this.response.appendInt((int) remaining); } + */ return this.response; } diff --git a/src/main/java/com/eu/habbo/plugin/PluginManager.java b/src/main/java/com/eu/habbo/plugin/PluginManager.java index 52df1e66..041f835d 100644 --- a/src/main/java/com/eu/habbo/plugin/PluginManager.java +++ b/src/main/java/com/eu/habbo/plugin/PluginManager.java @@ -21,8 +21,11 @@ import com.eu.habbo.habbohotel.navigation.EventCategory; import com.eu.habbo.habbohotel.navigation.NavigatorManager; import com.eu.habbo.habbohotel.pets.PetManager; import com.eu.habbo.habbohotel.rooms.*; +import com.eu.habbo.habbohotel.users.clothingvalidation.ClothingValidationManager; import com.eu.habbo.habbohotel.users.HabboInventory; import com.eu.habbo.habbohotel.users.HabboManager; +import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionHabboClub; +import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionManager; import com.eu.habbo.habbohotel.wired.WiredHandler; import com.eu.habbo.habbohotel.wired.highscores.WiredHighscoreManager; import com.eu.habbo.messages.PacketManager; @@ -59,6 +62,7 @@ import java.net.URLClassLoader; import java.util.Arrays; import java.util.NoSuchElementException; import java.util.Objects; +import java.util.regex.Pattern; import java.util.stream.Collectors; public class PluginManager { @@ -97,8 +101,8 @@ public class PluginManager { Bot.PLACEMENT_MESSAGES = Emulator.getConfig().getValue("hotel.bot.placement.messages", "Yo!;Hello I'm a real party animal!;Hello!").split(";"); HabboInventory.MAXIMUM_ITEMS = Emulator.getConfig().getInt("hotel.inventory.max.items"); - Messenger.MAXIMUM_FRIENDS = Emulator.getConfig().getInt("hotel.max.friends"); - Messenger.MAXIMUM_FRIENDS_HC = Emulator.getConfig().getInt("hotel.max.friends.hc"); + Messenger.MAXIMUM_FRIENDS = Emulator.getConfig().getInt("hotel.users.max.friends", 300); + Messenger.MAXIMUM_FRIENDS_HC = Emulator.getConfig().getInt("hotel.users.max.friends.hc", 1100); Room.MAXIMUM_BOTS = Emulator.getConfig().getInt("hotel.max.bots.room"); Room.MAXIMUM_PETS = Emulator.getConfig().getInt("hotel.pets.max.room"); Room.MAXIMUM_FURNI = Emulator.getConfig().getInt("hotel.room.furni.max", 2500); @@ -107,8 +111,8 @@ public class PluginManager { Room.IDLE_CYCLES = Emulator.getConfig().getInt("hotel.roomuser.idle.cycles", 240); Room.IDLE_CYCLES_KICK = Emulator.getConfig().getInt("hotel.roomuser.idle.cycles.kick", 480); Room.ROLLERS_MAXIMUM_ROLL_AVATARS = Emulator.getConfig().getInt("hotel.room.rollers.roll_avatars.max", 1); - RoomManager.MAXIMUM_ROOMS_VIP = Emulator.getConfig().getInt("hotel.max.rooms.vip"); - RoomManager.MAXIMUM_ROOMS_USER = Emulator.getConfig().getInt("hotel.max.rooms.user"); + RoomManager.MAXIMUM_ROOMS_USER = Emulator.getConfig().getInt("hotel.users.max.rooms", 50); + RoomManager.MAXIMUM_ROOMS_HC = Emulator.getConfig().getInt("hotel.users.max.rooms.hc", 75); RoomManager.HOME_ROOM_ID = Emulator.getConfig().getInt("hotel.home.room"); WiredHandler.MAXIMUM_FURNI_SELECTION = Emulator.getConfig().getInt("hotel.wired.furni.selection.count"); WiredHandler.TELEPORT_DELAY = Emulator.getConfig().getInt("wired.effect.teleport.delay", 500); @@ -162,6 +166,49 @@ public class PluginManager { PetManager.MAXIMUM_PET_INVENTORY_SIZE = Emulator.getConfig().getInt("hotel.pets.max.inventory"); + SubscriptionHabboClub.HC_PAYDAY_ENABLED = Emulator.getConfig().getBoolean("subscriptions.hc.payday.enabled", false); + + try { + SubscriptionHabboClub.HC_PAYDAY_NEXT_DATE = (int) (Emulator.stringToDate(Emulator.getConfig().getValue("subscriptions.hc.payday.next_date")).getTime() / 1000); + } + catch(Exception e) { SubscriptionHabboClub.HC_PAYDAY_NEXT_DATE = Integer.MAX_VALUE; } + + SubscriptionHabboClub.HC_PAYDAY_INTERVAL = Emulator.getConfig().getValue("subscriptions.hc.payday.interval"); + SubscriptionHabboClub.HC_PAYDAY_QUERY = Emulator.getConfig().getValue("subscriptions.hc.payday.query"); + SubscriptionHabboClub.HC_PAYDAY_CURRENCY = Emulator.getConfig().getValue("subscriptions.hc.payday.currency"); + SubscriptionHabboClub.HC_PAYDAY_KICKBACK_PERCENTAGE = Emulator.getConfig().getInt("subscriptions.hc.payday.percentage", 10) / 100.0; + SubscriptionHabboClub.HC_PAYDAY_COINSSPENT_RESET_ON_EXPIRE = Emulator.getConfig().getBoolean("subscriptions.hc.payday.creditsspent_reset_on_expire", false); + + SubscriptionHabboClub.HC_PAYDAY_STREAK.clear(); + for (String streak : Emulator.getConfig().getValue("subscriptions.hc.payday.streak", "7=5;30=10;60=15;90=20;180=25;365=30").split(Pattern.quote(";"))) { + if(streak.contains("=")) { + SubscriptionHabboClub.HC_PAYDAY_STREAK.put(Integer.parseInt(streak.split(Pattern.quote("="))[0]), Integer.parseInt(streak.split(Pattern.quote("="))[1])); + } + } + + ClothingValidationManager.VALIDATE_ON_HC_EXPIRE = Emulator.getConfig().getBoolean("hotel.users.clothingvalidation.onhcexpired", false); + ClothingValidationManager.VALIDATE_ON_LOGIN = Emulator.getConfig().getBoolean("hotel.users.clothingvalidation.onlogin", false); + ClothingValidationManager.VALIDATE_ON_CHANGE_LOOKS = Emulator.getConfig().getBoolean("hotel.users.clothingvalidation.onchangelooks", false); + ClothingValidationManager.VALIDATE_ON_MIMIC = Emulator.getConfig().getBoolean("hotel.users.clothingvalidation.onmimic", false); + ClothingValidationManager.VALIDATE_ON_MANNEQUIN = Emulator.getConfig().getBoolean("hotel.users.clothingvalidation.onmannequin", false); + ClothingValidationManager.VALIDATE_ON_FBALLGATE = Emulator.getConfig().getBoolean("hotel.users.clothingvalidation.onfballgate", false); + + String newUrl = Emulator.getConfig().getValue("gamedata.figuredata.url"); + if(!ClothingValidationManager.FIGUREDATA_URL.equals(newUrl)) { + ClothingValidationManager.FIGUREDATA_URL = newUrl; + ClothingValidationManager.reloadFiguredata(newUrl); + } + + if(newUrl.isEmpty()) { + ClothingValidationManager.VALIDATE_ON_HC_EXPIRE = false; + ClothingValidationManager.VALIDATE_ON_LOGIN = false; + ClothingValidationManager.VALIDATE_ON_CHANGE_LOOKS = false; + ClothingValidationManager.VALIDATE_ON_MIMIC = false; + ClothingValidationManager.VALIDATE_ON_MANNEQUIN = false; + ClothingValidationManager.VALIDATE_ON_FBALLGATE = false; + } + + NewNavigatorEventCategoriesComposer.CATEGORIES.clear(); for (String category : Emulator.getConfig().getValue("navigator.eventcategories", "").split(";")) { try { @@ -179,6 +226,7 @@ public class PluginManager { Emulator.getGameEnvironment().getPointsScheduler().reloadConfig(); Emulator.getGameEnvironment().getPixelScheduler().reloadConfig(); Emulator.getGameEnvironment().getGotwPointsScheduler().reloadConfig(); + Emulator.getGameEnvironment().subscriptionScheduler.reloadConfig(); } } diff --git a/src/main/java/com/eu/habbo/plugin/events/users/subscriptions/UserSubscriptionCreatedEvent.java b/src/main/java/com/eu/habbo/plugin/events/users/subscriptions/UserSubscriptionCreatedEvent.java new file mode 100644 index 00000000..6a6ce5ee --- /dev/null +++ b/src/main/java/com/eu/habbo/plugin/events/users/subscriptions/UserSubscriptionCreatedEvent.java @@ -0,0 +1,17 @@ +package com.eu.habbo.plugin.events.users.subscriptions; + +import com.eu.habbo.plugin.Event; + +public class UserSubscriptionCreatedEvent extends Event { + public final int userId; + public final String subscriptionType; + public final int duration; + + public UserSubscriptionCreatedEvent(int userId, String subscriptionType, int duration) { + super(); + + this.userId = userId; + this.subscriptionType = subscriptionType; + this.duration = duration; + } +} diff --git a/src/main/java/com/eu/habbo/plugin/events/users/subscriptions/UserSubscriptionExpiredEvent.java b/src/main/java/com/eu/habbo/plugin/events/users/subscriptions/UserSubscriptionExpiredEvent.java new file mode 100644 index 00000000..7090066c --- /dev/null +++ b/src/main/java/com/eu/habbo/plugin/events/users/subscriptions/UserSubscriptionExpiredEvent.java @@ -0,0 +1,16 @@ +package com.eu.habbo.plugin.events.users.subscriptions; + +import com.eu.habbo.habbohotel.users.subscriptions.Subscription; +import com.eu.habbo.plugin.Event; + +public class UserSubscriptionExpiredEvent extends Event { + public final int userId; + public final Subscription subscription; + + public UserSubscriptionExpiredEvent(int userId, Subscription subscription) { + super(); + + this.userId = userId; + this.subscription = subscription; + } +} diff --git a/src/main/java/com/eu/habbo/plugin/events/users/subscriptions/UserSubscriptionExtendedEvent.java b/src/main/java/com/eu/habbo/plugin/events/users/subscriptions/UserSubscriptionExtendedEvent.java new file mode 100644 index 00000000..3e1c24e0 --- /dev/null +++ b/src/main/java/com/eu/habbo/plugin/events/users/subscriptions/UserSubscriptionExtendedEvent.java @@ -0,0 +1,17 @@ +package com.eu.habbo.plugin.events.users.subscriptions; + +import com.eu.habbo.habbohotel.users.subscriptions.Subscription; +import com.eu.habbo.plugin.Event; + +public class UserSubscriptionExtendedEvent extends Event { + public final int userId; + public final Subscription subscription; + public final int duration; + + public UserSubscriptionExtendedEvent(int userId, Subscription subscription, int duration) { + super(); + this.userId = userId; + this.subscription = subscription; + this.duration = duration; + } +} From df20ea3e32c498be58f049a6274dd7ad63d78c81 Mon Sep 17 00:00:00 2001 From: Beny Date: Mon, 5 Oct 2020 03:13:09 +0200 Subject: [PATCH 2/5] Subscription RCON packets, HC Currency modifiers, HC gifts, HC discount, HC achievement --- sqlupdates/DEV_TO_SUBSCRIPTION-REVAMP.sql | 29 +++-- .../com/eu/habbo/core/CreditsScheduler.java | 5 +- .../eu/habbo/core/GotwPointsScheduler.java | 5 +- .../com/eu/habbo/core/PixelScheduler.java | 4 +- .../com/eu/habbo/core/PointsScheduler.java | 4 +- .../habbohotel/catalog/CatalogManager.java | 10 ++ .../habbo/habbohotel/catalog/ClubOffer.java | 49 +++++++- .../commands/SubscriptionCommand.java | 10 +- .../com/eu/habbo/habbohotel/items/Item.java | 30 ++++- .../habbo/habbohotel/items/ItemManager.java | 33 +++--- .../interactions/InteractionCrackable.java | 11 +- .../eu/habbo/habbohotel/users/HabboStats.java | 29 ++++- .../clothingvalidation/FiguredataPalette.java | 8 +- .../clothingvalidation/FiguredataSettype.java | 12 +- .../subscriptions/SubscriptionHabboClub.java | 82 ++++++++++--- .../com/eu/habbo/messages/PacketManager.java | 4 + .../eu/habbo/messages/incoming/Incoming.java | 4 + .../catalog/CatalogBuyClubDiscountEvent.java | 88 ++++++++++++++ .../incoming/catalog/CatalogBuyItemEvent.java | 19 +-- .../CatalogRequestClubDiscountEvent.java | 54 +++++++++ .../catalog/CatalogSelectClubGiftEvent.java | 77 +++++++++++++ .../catalog/RequestClubDataEvent.java | 1 - .../catalog/RequestClubGiftsEvent.java | 6 +- .../incoming/handshake/SecureLoginEvent.java | 2 +- .../users/RequestClubCenterEvent.java | 12 ++ .../incoming/users/RequestUserClubEvent.java | 3 +- .../outgoing/catalog/ClubDataComposer.java | 40 +------ .../outgoing/catalog/ClubGiftsComposer.java | 71 ++++++------ .../unknown/ExtendClubMessageComposer.java | 85 +++----------- .../users/ClubGiftReceivedComposer.java | 18 +-- .../outgoing/users/UserClubComposer.java | 59 +++++++++- .../messages/rcon/ModifyUserSubscription.java | 108 ++++++++++++++++++ .../networking/rconserver/RCONServer.java | 1 + .../com/eu/habbo/plugin/PluginManager.java | 3 + 34 files changed, 728 insertions(+), 248 deletions(-) create mode 100644 src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogBuyClubDiscountEvent.java create mode 100644 src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogRequestClubDiscountEvent.java create mode 100644 src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogSelectClubGiftEvent.java create mode 100644 src/main/java/com/eu/habbo/messages/incoming/users/RequestClubCenterEvent.java create mode 100644 src/main/java/com/eu/habbo/messages/rcon/ModifyUserSubscription.java diff --git a/sqlupdates/DEV_TO_SUBSCRIPTION-REVAMP.sql b/sqlupdates/DEV_TO_SUBSCRIPTION-REVAMP.sql index a1686879..5003f7fa 100644 --- a/sqlupdates/DEV_TO_SUBSCRIPTION-REVAMP.sql +++ b/sqlupdates/DEV_TO_SUBSCRIPTION-REVAMP.sql @@ -53,7 +53,9 @@ INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.hc.payday INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.hc.payday.currency', 'credits'); INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.hc.payday.percentage', '10'); INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.hc.payday.creditsspent_reset_on_expire', '1'); -INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('subscriptions.hc.payday.message', 'Woohoo HC Payday has arrived! You have received %amount% credits to your purse. Enjoy!') +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.hc.achievement', 'VipHC'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.hc.discount.enabled', '1'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.hc.discount.days_before_end', '7'); INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.scheduler.enabled', '1'); INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('subscriptions.scheduler.interval', '10'); @@ -71,18 +73,31 @@ INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.users.max.friends INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.users.max.rooms', '50'); INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.users.max.rooms.hc', '75'); -DELETE FROM `emulator_settings` WHERE `key` = 'hotel.max.rooms.per.user'; -DELETE FROM `emulator_settings` WHERE `key` = 'hotel.max.rooms.user'; -DELETE FROM `emulator_settings` WHERE `key` = 'hotel.max.rooms.vip'; +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.auto.pixels.hc_modifier', '1'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.auto.points.hc_modifier', '1'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.auto.credits.hc_modifier', '1'); +INSERT INTO `emulator_settings`(`key`, `value`) VALUES ('hotel.auto.gotwpoints.hc_modifier', '1'); -DELETE FROM `emulator_settings` WHERE `key` = 'max.friends'; -DELETE FROM `emulator_settings` WHERE `key` = 'max.friends'; +DELETE FROM `emulator_settings` WHERE `key` IN ('hotel.max.rooms.per.user', 'hotel.max.rooms.user', 'hotel.max.rooms.vip', 'max.friends', 'hotel.max.friends', 'max.friends.hc', 'hotel.max.friends.hc'); ALTER TABLE `users_settings` ADD COLUMN `max_friends` int(10) NULL DEFAULT 300 AFTER `has_gotten_default_saved_searches`; ALTER TABLE `users_settings` ADD COLUMN `max_rooms` int(10) NULL DEFAULT 50 AFTER `has_gotten_default_saved_searches`; ALTER TABLE `users_settings` ADD COLUMN `last_hc_payday` int(10) NULL DEFAULT 0 AFTER `has_gotten_default_saved_searches`; +ALTER TABLE `users_settings` ADD COLUMN `hc_gifts_claimed` int(10) NULL DEFAULT 0 AFTER `has_gotten_default_saved_searches`; ALTER TABLE `permissions` ADD COLUMN `cmd_subscription` enum('0','1') NULL DEFAULT '0' AFTER `cmd_credits`; INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.keys.cmd_subscription', 'subscription;sub'); +INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.error.cmd_subscription.invalid_action', 'Invalid action specified. Must be add, +, remove or -'); +INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.error.cmd_subscription.type_not_found', '%subscription% is not a valid subscription type'); +INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.error.cmd_subscription.invalid_params_time', 'Invalid time span, try: x minutes/days/weeks/months'); +INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.error.cmd_subscription.success_add_time', 'Successfully added %time% seconds to %subscription% on %user%'); +INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.error.cmd_subscription.user_not_have', '%user% does not have the %subscription% subscription'); +INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.error.cmd_subscription.success_remove_time', 'Successfully removed %time% seconds from %subscription% on %user%'); +INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.error.cmd_subscription.success_remove_sub', 'Successfully removed %subscription% sub from %user%'); +INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.error.cmd_subscription.user_not_found', '%user% was not found'); +INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('commands.error.cmd_subscription.invalid_params', 'Invalid command format'); -INSERT INTO users_subscriptions SELECT NULL, user_id, 'HABBO_CLUB' as `subscription_type`, UNIX_TIMESTAMP() AS `timestamp_start`, (club_expire_timestamp - UNIX_TIMESTAMP()) AS `duration`, 1 AS `active` FROM users_settings WHERE club_expire_timestamp > UNIX_TIMESTAMP(); +INSERT INTO `emulator_texts` (`key`, `value`) VALUES ('subscriptions.hc.payday.message', 'Woohoo HC Payday has arrived! You have received %amount% credits to your purse. Enjoy!'); + +-- OPTIONAL HC MIGRATION +-- INSERT INTO users_subscriptions SELECT NULL, user_id, 'HABBO_CLUB' as `subscription_type`, UNIX_TIMESTAMP() AS `timestamp_start`, (club_expire_timestamp - UNIX_TIMESTAMP()) AS `duration`, 1 AS `active` FROM users_settings WHERE club_expire_timestamp > UNIX_TIMESTAMP(); diff --git a/src/main/java/com/eu/habbo/core/CreditsScheduler.java b/src/main/java/com/eu/habbo/core/CreditsScheduler.java index d5442bbf..db5e74fb 100644 --- a/src/main/java/com/eu/habbo/core/CreditsScheduler.java +++ b/src/main/java/com/eu/habbo/core/CreditsScheduler.java @@ -13,6 +13,7 @@ public class CreditsScheduler extends Scheduler { public static boolean IGNORE_HOTEL_VIEW; public static boolean IGNORE_IDLED; + public static double HC_MODIFIER; public CreditsScheduler() { @@ -24,6 +25,8 @@ public class CreditsScheduler extends Scheduler { if (Emulator.getConfig().getBoolean("hotel.auto.credits.enabled")) { IGNORE_HOTEL_VIEW = Emulator.getConfig().getBoolean("hotel.auto.credits.ignore.hotelview"); IGNORE_IDLED = Emulator.getConfig().getBoolean("hotel.auto.credits.ignore.idled"); + HC_MODIFIER = Emulator.getConfig().getDouble("hotel.auto.credits.hc_modifier", 1.0); + if (this.disposed) { this.disposed = false; this.run(); @@ -49,7 +52,7 @@ public class CreditsScheduler extends Scheduler { if (habbo.getRoomUnit().isIdle() && IGNORE_IDLED) continue; - habbo.giveCredits(habbo.getHabboInfo().getRank().getCreditsTimerAmount()); + habbo.giveCredits((int)(habbo.getHabboInfo().getRank().getCreditsTimerAmount() * (habbo.getHabboStats().hasActiveClub() ? HC_MODIFIER : 1.0))); } } catch (Exception e) { LOGGER.error("Caught exception", e); diff --git a/src/main/java/com/eu/habbo/core/GotwPointsScheduler.java b/src/main/java/com/eu/habbo/core/GotwPointsScheduler.java index 58b3acc7..58350a95 100644 --- a/src/main/java/com/eu/habbo/core/GotwPointsScheduler.java +++ b/src/main/java/com/eu/habbo/core/GotwPointsScheduler.java @@ -14,6 +14,7 @@ public class GotwPointsScheduler extends Scheduler { public static boolean IGNORE_HOTEL_VIEW; public static boolean IGNORE_IDLED; public static String GOTW_POINTS_NAME; + public static double HC_MODIFIER; public GotwPointsScheduler() { //TODO MOVE TO A PLUGIN. IS NOT PART OF OFFICIAL HABBO. @@ -25,6 +26,7 @@ public class GotwPointsScheduler extends Scheduler { if (Emulator.getConfig().getBoolean("hotel.auto.gotwpoints.enabled")) { IGNORE_HOTEL_VIEW = Emulator.getConfig().getBoolean("hotel.auto.gotwpoints.ignore.hotelview"); IGNORE_IDLED = Emulator.getConfig().getBoolean("hotel.auto.gotwpoints.ignore.idled"); + HC_MODIFIER = Emulator.getConfig().getDouble("hotel.auto.gotwpoints.hc_modifier", 1.0); GOTW_POINTS_NAME = Emulator.getConfig().getValue("hotel.auto.gotwpoints.name"); if (this.disposed) { @@ -62,8 +64,7 @@ public class GotwPointsScheduler extends Scheduler { } type = Emulator.getConfig().getInt("seasonal.currency." + GOTW_POINTS_NAME, -1); if (found || type != -1) { - - habbo.givePoints(type, habbo.getHabboInfo().getRank().getGotwTimerAmount()); + habbo.givePoints(type, (int)(habbo.getHabboInfo().getRank().getGotwTimerAmount() * (habbo.getHabboStats().hasActiveClub() ? HC_MODIFIER : 1.0))); } } } catch (Exception e) { diff --git a/src/main/java/com/eu/habbo/core/PixelScheduler.java b/src/main/java/com/eu/habbo/core/PixelScheduler.java index cadf07ea..2b0bddd5 100644 --- a/src/main/java/com/eu/habbo/core/PixelScheduler.java +++ b/src/main/java/com/eu/habbo/core/PixelScheduler.java @@ -13,6 +13,7 @@ public class PixelScheduler extends Scheduler { public static boolean IGNORE_HOTEL_VIEW; public static boolean IGNORE_IDLED; + public static double HC_MODIFIER; public PixelScheduler() { super(Emulator.getConfig().getInt("hotel.auto.pixels.interval")); @@ -23,6 +24,7 @@ public class PixelScheduler extends Scheduler { if (Emulator.getConfig().getBoolean("hotel.auto.pixels.enabled")) { IGNORE_HOTEL_VIEW = Emulator.getConfig().getBoolean("hotel.auto.pixels.ignore.hotelview"); IGNORE_IDLED = Emulator.getConfig().getBoolean("hotel.auto.pixels.ignore.idled"); + HC_MODIFIER = Emulator.getConfig().getDouble("hotel.auto.pixels.hc_modifier", 1.0); if (this.disposed) { this.disposed = false; this.run(); @@ -47,7 +49,7 @@ public class PixelScheduler extends Scheduler { if (habbo.getRoomUnit().isIdle() && IGNORE_IDLED) continue; - habbo.givePixels(habbo.getHabboInfo().getRank().getPixelsTimerAmount()); + habbo.givePixels((int)(habbo.getHabboInfo().getRank().getPixelsTimerAmount() * (habbo.getHabboStats().hasActiveClub() ? HC_MODIFIER : 1.0))); } } catch (Exception e) { LOGGER.error("Caught exception", e); diff --git a/src/main/java/com/eu/habbo/core/PointsScheduler.java b/src/main/java/com/eu/habbo/core/PointsScheduler.java index d30abc6a..ca863cb8 100644 --- a/src/main/java/com/eu/habbo/core/PointsScheduler.java +++ b/src/main/java/com/eu/habbo/core/PointsScheduler.java @@ -13,6 +13,7 @@ public class PointsScheduler extends Scheduler { public static boolean IGNORE_HOTEL_VIEW; public static boolean IGNORE_IDLED; + public static double HC_MODIFIER; public PointsScheduler() { @@ -24,6 +25,7 @@ public class PointsScheduler extends Scheduler { if (Emulator.getConfig().getBoolean("hotel.auto.points.enabled")) { IGNORE_HOTEL_VIEW = Emulator.getConfig().getBoolean("hotel.auto.points.ignore.hotelview"); IGNORE_IDLED = Emulator.getConfig().getBoolean("hotel.auto.points.ignore.idled"); + HC_MODIFIER = Emulator.getConfig().getDouble("hotel.auto.points.hc_modifier", 1.0); if (this.disposed) { this.disposed = false; this.run(); @@ -50,7 +52,7 @@ public class PointsScheduler extends Scheduler { continue; //habbo.givePoints(POINTS); - habbo.givePoints(habbo.getHabboInfo().getRank().getDiamondsTimerAmount()); + habbo.givePoints((int)(habbo.getHabboInfo().getRank().getDiamondsTimerAmount() * (habbo.getHabboStats().hasActiveClub() ? HC_MODIFIER : 1.0))); } } catch (Exception e) { LOGGER.error("Caught exception", e); diff --git a/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogManager.java b/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogManager.java index 87649d94..34a24e3d 100644 --- a/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogManager.java +++ b/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogManager.java @@ -615,6 +615,16 @@ public class CatalogManager { .findAny().orElse(null); } + public CatalogPage getCatalogPageByLayout(String layoutName) { + return this.catalogPages.valueCollection().stream() + .filter(p -> p != null && + p.isVisible() && + p.isEnabled() && + p.getRank() < 2 && + p.getLayout() != null && p.getLayout().equalsIgnoreCase(layoutName) + ) + .findAny().orElse(null); + } public CatalogItem getCatalogItem(int id) { final CatalogItem[] item = {null}; diff --git a/src/main/java/com/eu/habbo/habbohotel/catalog/ClubOffer.java b/src/main/java/com/eu/habbo/habbohotel/catalog/ClubOffer.java index 4ba103ba..de40e8ef 100644 --- a/src/main/java/com/eu/habbo/habbohotel/catalog/ClubOffer.java +++ b/src/main/java/com/eu/habbo/habbohotel/catalog/ClubOffer.java @@ -1,9 +1,14 @@ package com.eu.habbo.habbohotel.catalog; +import com.eu.habbo.Emulator; +import com.eu.habbo.messages.ISerialize; +import com.eu.habbo.messages.ServerMessage; + import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Calendar; -public class ClubOffer { +public class ClubOffer implements ISerialize { private final int id; @@ -70,4 +75,46 @@ public class ClubOffer { public boolean isDeal() { return this.deal; } + + @Override + public void serialize(ServerMessage message) { + serialize(message, Emulator.getIntUnixTimestamp()); + } + + public void serialize(ServerMessage message, int hcExpireTimestamp) { + hcExpireTimestamp = Math.min(Emulator.getIntUnixTimestamp(), hcExpireTimestamp); + message.appendInt(this.id); + message.appendString(this.name); + message.appendBoolean(false); //unused + message.appendInt(this.credits); + message.appendInt(this.points); + message.appendInt(this.pointsType); + message.appendBoolean(this.vip); + + long seconds = this.days * 86400L; + + long secondsTotal = seconds; + + int totalYears = (int) Math.floor((int) seconds / 86400 * 31 * 12); + seconds -= totalYears * 86400 * 31 * 12; + + int totalMonths = (int) Math.floor((int) seconds / 86400 * 31); + seconds -= totalMonths * 86400 * 31; + + int totalDays = (int) Math.floor((int) seconds / 86400); + seconds -= totalDays * 86400; + + message.appendInt((int) secondsTotal / 86400 / 31); + message.appendInt((int) seconds); + message.appendBoolean(false); //giftable + message.appendInt((int) seconds); + + hcExpireTimestamp += secondsTotal; + + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(hcExpireTimestamp * 1000L); + message.appendInt(cal.get(Calendar.YEAR)); + message.appendInt(cal.get(Calendar.MONTH)); + message.appendInt(cal.get(Calendar.DAY_OF_MONTH)); + } } \ No newline at end of file diff --git a/src/main/java/com/eu/habbo/habbohotel/commands/SubscriptionCommand.java b/src/main/java/com/eu/habbo/habbohotel/commands/SubscriptionCommand.java index fbf32b54..25f8b758 100644 --- a/src/main/java/com/eu/habbo/habbohotel/commands/SubscriptionCommand.java +++ b/src/main/java/com/eu/habbo/habbohotel/commands/SubscriptionCommand.java @@ -63,14 +63,13 @@ public class SubscriptionCommand extends Command { int timeToAdd = Emulator.timeStringToSeconds(message.toString()); if(timeToAdd < 1) { - gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.invalid_params", "Invalid time span, try: x minutes/days/weeks/months"), RoomChatMessageBubbles.ALERT); + gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.invalid_params_time", "Invalid time span, try: x minutes/days/weeks/months"), RoomChatMessageBubbles.ALERT); return true; } habbo.getHabboStats().createSubscription(subscription, timeToAdd); gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.success_add_time", "Successfully added %time% seconds to %subscription% on %user%").replace("%time%", timeToAdd + "").replace("%user%", params[1]).replace("%subscription%", subscription), RoomChatMessageBubbles.ALERT); } - else if(action.equalsIgnoreCase("remove") || action.equalsIgnoreCase("-") || action.equalsIgnoreCase("r")) { Subscription s = habbo.getHabboStats().getSubscription(subscription); @@ -83,11 +82,11 @@ public class SubscriptionCommand extends Command { int timeToRemove = Emulator.timeStringToSeconds(message.toString()); if (timeToRemove < 1) { - gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.invalid_params", "Invalid time span, try: x minutes/days/weeks/months"), RoomChatMessageBubbles.ALERT); + gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.invalid_params_time", "Invalid time span, try: x minutes/days/weeks/months"), RoomChatMessageBubbles.ALERT); return true; } - s.addDuration(timeToRemove); + s.addDuration(-timeToRemove); gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.success_remove_time", "Successfully removed %time% seconds from %subscription% on %user%").replace("%time%", timeToRemove + "").replace("%user%", params[1]).replace("%subscription%", subscription), RoomChatMessageBubbles.ALERT); } else { @@ -95,6 +94,9 @@ public class SubscriptionCommand extends Command { gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.success_remove_sub", "Successfully removed %subscription% sub from %user%").replace("%user%", params[1]).replace("%subscription%", subscription), RoomChatMessageBubbles.ALERT); } } + else { + gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.invalid_action", "Invalid action specified. Must be add, +, remove or -"), RoomChatMessageBubbles.ALERT); + } } else { gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_subscription.user_not_found", "%user% was not found").replace("%user%", params[1]), RoomChatMessageBubbles.ALERT); diff --git a/src/main/java/com/eu/habbo/habbohotel/items/Item.java b/src/main/java/com/eu/habbo/habbohotel/items/Item.java index eb7316ff..97e1a435 100644 --- a/src/main/java/com/eu/habbo/habbohotel/items/Item.java +++ b/src/main/java/com/eu/habbo/habbohotel/items/Item.java @@ -3,12 +3,14 @@ package com.eu.habbo.habbohotel.items; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.items.interactions.InteractionMultiHeight; import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.messages.ISerialize; +import com.eu.habbo.messages.ServerMessage; import gnu.trove.list.array.TIntArrayList; import java.sql.ResultSet; import java.sql.SQLException; -public class Item { +public class Item implements ISerialize { private int id; private int spriteId; @@ -220,4 +222,30 @@ public class Item { } public String getClothingOnWalk() { return clothingOnWalk; } + + @Override + public void serialize(ServerMessage message) { + message.appendString(this.type.code.toLowerCase()); + + if (type == FurnitureType.BADGE) { + message.appendString(this.customParams); + } else { + message.appendInt(this.spriteId); + + if (this.getName().contains("wallpaper_single") || this.getName().contains("floor_single") || this.getName().contains("landscape_single")) { + message.appendString(this.name.split("_")[2]); + } else if (type == FurnitureType.ROBOT) { + message.appendString(this.customParams); + } else if (name.equalsIgnoreCase("poster")) { + message.appendString(this.customParams); + } else if (name.startsWith("SONG ")) { + message.appendString(this.customParams); + } else { + message.appendString(""); + } + + message.appendInt(1); // productCount + message.appendBoolean(false); + } + } } diff --git a/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java b/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java index 7fdc0202..3da8125f 100644 --- a/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java +++ b/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java @@ -685,13 +685,14 @@ public class ItemManager { } public HabboItem createGift(String username, Item item, String extraData, int limitedStack, int limitedSells) { - HabboItem gift = null; - int userId = 0; Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(username); + int userId = 0; + if (habbo != null) { userId = habbo.getHabboInfo().getId(); - } else { + } + else { try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT id FROM users WHERE username = ?")) { statement.setString(1, username); try (ResultSet set = statement.executeQuery()) { @@ -704,6 +705,14 @@ public class ItemManager { } } + if(userId > 0) { + return createGift(userId, item, extraData, limitedStack, limitedSells); + } + + return null; + } + + public HabboItem createGift(int userId, Item item, String extraData, int limitedStack, int limitedSells) { if (userId == 0) return null; @@ -712,26 +721,12 @@ public class ItemManager { extraData = extraData.substring(0, 1000); } - try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO items (user_id, item_id, extra_data, limited_data) VALUES (?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS)) { - statement.setInt(1, userId); - statement.setInt(2, item.getId()); - statement.setString(3, extraData); - statement.setString(4, limitedStack + ":" + limitedSells); - statement.execute(); - - try (ResultSet set = statement.getGeneratedKeys()) { - if (set.next()) { - gift = new InteractionGift(set.getInt(1), userId, item, extraData, limitedStack, limitedSells); - } - } - } catch (SQLException e) { - LOGGER.error("Caught SQL exception", e); - } + HabboItem gift = this.createItem(userId, item, limitedStack, limitedSells, extraData); if (gift != null) { + Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(userId); if (habbo != null) { habbo.getInventory().getItemsComponent().addItem(gift); - habbo.getClient().sendResponse(new AddHabboItemComposer(gift)); } } diff --git a/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionCrackable.java b/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionCrackable.java index 70869154..af65156c 100644 --- a/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionCrackable.java +++ b/src/main/java/com/eu/habbo/habbohotel/items/interactions/InteractionCrackable.java @@ -10,6 +10,7 @@ import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboGender; import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionHabboClub; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.outgoing.users.UserClubComposer; import com.eu.habbo.messages.outgoing.users.UserPermissionsComposer; @@ -120,16 +121,10 @@ public class InteractionCrackable extends HabboItem { // subscriptions are given immediately upon cracking switch (rewardData.subscriptionType) { case HABBO_CLUB: - if (habbo.getHabboStats().getClubExpireTimestamp() <= Emulator.getIntUnixTimestamp()) - habbo.getHabboStats().setClubExpireTimestamp(Emulator.getIntUnixTimestamp()); - - habbo.getHabboStats().setClubExpireTimestamp(habbo.getHabboStats().getClubExpireTimestamp() + (rewardData.subscriptionDuration * 86400)); - habbo.getClient().sendResponse(new UserPermissionsComposer(habbo)); - habbo.getClient().sendResponse(new UserClubComposer(habbo)); - habbo.getHabboStats().run(); + habbo.getHabboStats().createSubscription(SubscriptionHabboClub.HABBO_CLUB, rewardData.subscriptionDuration * 86400); break; case BUILDERS_CLUB: - habbo.alert("Builders club has not been implemented yet. Sorry!"); + habbo.getHabboStats().createSubscription("BUILDERS_CLUB", rewardData.subscriptionDuration * 86400); break; } } diff --git a/src/main/java/com/eu/habbo/habbohotel/users/HabboStats.java b/src/main/java/com/eu/habbo/habbohotel/users/HabboStats.java index edf706a4..8f8f1382 100644 --- a/src/main/java/com/eu/habbo/habbohotel/users/HabboStats.java +++ b/src/main/java/com/eu/habbo/habbohotel/users/HabboStats.java @@ -10,7 +10,6 @@ import com.eu.habbo.habbohotel.rooms.RoomChatMessageBubbles; import com.eu.habbo.habbohotel.rooms.RoomTrade; import com.eu.habbo.habbohotel.users.cache.HabboOfferPurchase; import com.eu.habbo.habbohotel.users.subscriptions.Subscription; -import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionManager; import com.eu.habbo.plugin.events.users.subscriptions.UserSubscriptionCreatedEvent; import com.eu.habbo.plugin.events.users.subscriptions.UserSubscriptionExtendedEvent; import gnu.trove.list.array.TIntArrayList; @@ -96,6 +95,7 @@ public class HabboStats implements Runnable { public int maxFriends; public int maxRooms; public int lastHCPayday; + public int hcGiftsClaimed; public int hcMessageLastModified = Emulator.getIntUnixTimestamp(); public THashSet subscriptions; @@ -150,6 +150,7 @@ public class HabboStats implements Runnable { this.maxFriends = set.getInt("max_friends"); this.maxRooms = set.getInt("max_rooms"); this.lastHCPayday = set.getInt("last_hc_payday"); + this.hcGiftsClaimed = set.getInt("hc_gifts_claimed"); this.nuxReward = this.nux; @@ -316,7 +317,7 @@ public class HabboStats implements Runnable { int onlineTime = Emulator.getIntUnixTimestamp() - onlineTimeLast; try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) { - try (PreparedStatement statement = connection.prepareStatement("UPDATE users_settings SET achievement_score = ?, respects_received = ?, respects_given = ?, daily_respect_points = ?, block_following = ?, block_friendrequests = ?, online_time = online_time + ?, guild_id = ?, daily_pet_respect_points = ?, club_expire_timestamp = ?, login_streak = ?, rent_space_id = ?, rent_space_endtime = ?, volume_system = ?, volume_furni = ?, volume_trax = ?, block_roominvites = ?, old_chat = ?, block_camera_follow = ?, chat_color = ?, hof_points = ?, block_alerts = ?, talent_track_citizenship_level = ?, talent_track_helpers_level = ?, ignore_bots = ?, ignore_pets = ?, nux = ?, mute_end_timestamp = ?, allow_name_change = ?, perk_trade = ?, can_trade = ?, `forums_post_count` = ?, ui_flags = ?, has_gotten_default_saved_searches = ?, max_friends = ?, max_rooms = ?, last_hc_payday = ? WHERE user_id = ? LIMIT 1")) { + try (PreparedStatement statement = connection.prepareStatement("UPDATE users_settings SET achievement_score = ?, respects_received = ?, respects_given = ?, daily_respect_points = ?, block_following = ?, block_friendrequests = ?, online_time = online_time + ?, guild_id = ?, daily_pet_respect_points = ?, club_expire_timestamp = ?, login_streak = ?, rent_space_id = ?, rent_space_endtime = ?, volume_system = ?, volume_furni = ?, volume_trax = ?, block_roominvites = ?, old_chat = ?, block_camera_follow = ?, chat_color = ?, hof_points = ?, block_alerts = ?, talent_track_citizenship_level = ?, talent_track_helpers_level = ?, ignore_bots = ?, ignore_pets = ?, nux = ?, mute_end_timestamp = ?, allow_name_change = ?, perk_trade = ?, can_trade = ?, `forums_post_count` = ?, ui_flags = ?, has_gotten_default_saved_searches = ?, max_friends = ?, max_rooms = ?, last_hc_payday = ?, hc_gifts_claimed = ? WHERE user_id = ? LIMIT 1")) { statement.setInt(1, this.achievementScore); statement.setInt(2, this.respectPointsReceived); statement.setInt(3, this.respectPointsGiven); @@ -354,7 +355,8 @@ public class HabboStats implements Runnable { statement.setInt(35, this.maxFriends); statement.setInt(36, this.maxRooms); statement.setInt(37, this.lastHCPayday); - statement.setInt(38, this.habboInfo.getId()); + statement.setInt(38, this.hcGiftsClaimed); + statement.setInt(39, this.habboInfo.getId()); statement.executeUpdate(); } @@ -542,6 +544,27 @@ public class HabboStats implements Runnable { return hasSubscription(Subscription.HABBO_CLUB); } + public int getPastTimeAsClub() { + int pastTimeAsHC = 0; + for(Subscription subs : this.subscriptions) { + if(subs.getSubscriptionType().equalsIgnoreCase(Subscription.HABBO_CLUB)) { + pastTimeAsHC += subs.getDuration() - (Math.max(subs.getRemaining(), 0)); + } + } + return pastTimeAsHC; + } + + public int getTimeTillNextClubGift() { + int pastTimeAsClub = getPastTimeAsClub(); + int totalGifts = (int)Math.ceil(pastTimeAsClub / 2678400.0); + return (totalGifts * 2678400) - pastTimeAsClub; + } + + public int getRemainingClubGifts() { + int totalGifts = (int)Math.ceil(getPastTimeAsClub() / 2678400.0); + return totalGifts - this.hcGiftsClaimed; + } + public THashMap getAchievementProgress() { return this.achievementProgress; } diff --git a/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataPalette.java b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataPalette.java index b569d83f..841fd5d4 100644 --- a/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataPalette.java +++ b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataPalette.java @@ -1,11 +1,10 @@ package com.eu.habbo.habbohotel.users.clothingvalidation; -import java.util.Map; import java.util.TreeMap; public class FiguredataPalette { public int id; - public Map colors; + public TreeMap colors; public FiguredataPalette(int id) { this.id = id; @@ -20,11 +19,8 @@ public class FiguredataPalette { return this.colors.get(colorId); } - /** - * @return First non-club and selectable color - */ public FiguredataPaletteColor getFirstNonHCColor() { - for(FiguredataPaletteColor color : this.colors.values()) { + for(FiguredataPaletteColor color : this.colors.descendingMap().values()) { if(!color.club && color.selectable) return color; } diff --git a/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataSettype.java b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataSettype.java index dfe6366d..e3698815 100644 --- a/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataSettype.java +++ b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/FiguredataSettype.java @@ -10,7 +10,7 @@ public class FiguredataSettype { public boolean mandatoryFemale0; public boolean mandatoryMale1; public boolean mandatoryFemale1; - public Map sets; + public TreeMap sets; public FiguredataSettype(String type, int paletteId, boolean mandatoryMale0, boolean mandatoryFemale0, boolean mandatoryMale1, boolean mandatoryFemale1) { this.type = type; @@ -35,13 +35,13 @@ public class FiguredataSettype { * @return First non-sellable and selectable set for given gender */ public FiguredataSettypeSet getFirstSetForGender(String gender) { - for(FiguredataSettypeSet set : this.sets.values()) { - if(set.gender.equalsIgnoreCase(gender) && !set.sellable && set.selectable) { + for(FiguredataSettypeSet set : this.sets.descendingMap().values()) { + if((set.gender.equalsIgnoreCase(gender) || set.gender.equalsIgnoreCase("u")) && !set.sellable && set.selectable) { return set; } } - return this.sets.size() > 0 ? this.sets.entrySet().iterator().next().getValue() : null; + return this.sets.size() > 0 ? this.sets.descendingMap().entrySet().iterator().next().getValue() : null; } /** @@ -49,8 +49,8 @@ public class FiguredataSettype { * @return First non-club, non-sellable and selectable set for given gender */ public FiguredataSettypeSet getFirstNonHCSetForGender(String gender) { - for(FiguredataSettypeSet set : this.sets.values()) { - if(set.gender.equalsIgnoreCase(gender) && !set.club && !set.sellable && set.selectable) { + for(FiguredataSettypeSet set : this.sets.descendingMap().values()) { + if((set.gender.equalsIgnoreCase(gender) || set.gender.equalsIgnoreCase("u")) && !set.club && !set.sellable && set.selectable) { return set; } } diff --git a/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/SubscriptionHabboClub.java b/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/SubscriptionHabboClub.java index 32e04120..f0b1692b 100644 --- a/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/SubscriptionHabboClub.java +++ b/src/main/java/com/eu/habbo/habbohotel/users/subscriptions/SubscriptionHabboClub.java @@ -2,12 +2,16 @@ package com.eu.habbo.habbohotel.users.subscriptions; import com.eu.habbo.Emulator; import com.eu.habbo.database.Database; +import com.eu.habbo.habbohotel.achievements.Achievement; +import com.eu.habbo.habbohotel.achievements.AchievementManager; import com.eu.habbo.habbohotel.messenger.Messenger; import com.eu.habbo.habbohotel.rooms.RoomManager; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboInfo; +import com.eu.habbo.habbohotel.users.HabboStats; import com.eu.habbo.habbohotel.users.clothingvalidation.ClothingValidationManager; import com.eu.habbo.messages.outgoing.catalog.ClubCenterDataComposer; +import com.eu.habbo.messages.outgoing.generic.PickMonthlyClubGiftNotificationComposer; import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDataComposer; import com.eu.habbo.messages.outgoing.users.*; import gnu.trove.map.hash.THashMap; @@ -33,6 +37,9 @@ public class SubscriptionHabboClub extends Subscription { public static TreeMap HC_PAYDAY_STREAK = new TreeMap<>(); public static String HC_PAYDAY_CURRENCY = ""; public static Double HC_PAYDAY_KICKBACK_PERCENTAGE = 0.1; + public static String ACHIEVEMENT_NAME = ""; + public static boolean DISCOUNT_ENABLED = false; + public static int DISCOUNT_DAYS_BEFORE_END = 7; /** * When true "coins spent" will be calculated from the timestamp the user joins HC instead of from the last HC pay day execution timestamp @@ -60,19 +67,28 @@ public class SubscriptionHabboClub extends Subscription { public void onCreated() { super.onCreated(); - Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(this.getUserId()); - habbo.getHabboStats().maxFriends = Messenger.MAXIMUM_FRIENDS_HC; - habbo.getHabboStats().maxRooms = RoomManager.MAXIMUM_ROOMS_HC; - habbo.getHabboStats().lastHCPayday = HC_PAYDAY_COINSSPENT_RESET_ON_EXPIRE ? Emulator.getIntUnixTimestamp() : HC_PAYDAY_NEXT_DATE - Emulator.timeStringToSeconds(HC_PAYDAY_INTERVAL); - Emulator.getThreading().run(habbo.getHabboStats()); + HabboInfo habboInfo = Emulator.getGameEnvironment().getHabboManager().getHabboInfo(this.getUserId()); + HabboStats stats = habboInfo.getHabboStats(); - if(habbo.getClient() != null) { + stats.maxFriends = Messenger.MAXIMUM_FRIENDS_HC; + stats.maxRooms = RoomManager.MAXIMUM_ROOMS_HC; + stats.lastHCPayday = HC_PAYDAY_COINSSPENT_RESET_ON_EXPIRE ? Emulator.getIntUnixTimestamp() : HC_PAYDAY_NEXT_DATE - Emulator.timeStringToSeconds(HC_PAYDAY_INTERVAL); + Emulator.getThreading().run(stats); + + progressAchievement(habboInfo); + + Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(this.getUserId()); + if(habbo != null && habbo.getClient() != null) { + + if(habbo.getHabboStats().getRemainingClubGifts() > 0) { + habbo.getClient().sendResponse(new PickMonthlyClubGiftNotificationComposer(habbo.getHabboStats().getRemainingClubGifts())); + } if((Emulator.getIntUnixTimestamp() - habbo.getHabboStats().hcMessageLastModified) < 60) { Emulator.getThreading().run(() -> { habbo.getClient().sendResponse(new UserClubComposer(habbo)); habbo.getClient().sendResponse(new UserPermissionsComposer(habbo)); }, (Emulator.getIntUnixTimestamp() - habbo.getHabboStats().hcMessageLastModified)); } else { - habbo.getClient().sendResponse(new UserClubComposer(habbo)); + habbo.getClient().sendResponse(new UserClubComposer(habbo, SubscriptionHabboClub.HABBO_CLUB, UserClubComposer.RESPONSE_TYPE_NORMAL)); habbo.getClient().sendResponse(new UserPermissionsComposer(habbo)); } } @@ -91,7 +107,7 @@ public class SubscriptionHabboClub extends Subscription { if(amount < 0) { Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(this.getUserId()); if(habbo != null && habbo.getClient() != null) { - habbo.getClient().sendResponse(new UserClubComposer(habbo)); + habbo.getClient().sendResponse(new UserClubComposer(habbo, SubscriptionHabboClub.HABBO_CLUB, UserClubComposer.RESPONSE_TYPE_NORMAL)); habbo.getClient().sendResponse(new UserPermissionsComposer(habbo)); } } @@ -109,8 +125,8 @@ public class SubscriptionHabboClub extends Subscription { Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(this.getUserId()); - if(habbo.getClient() != null) { - habbo.getClient().sendResponse(new UserClubComposer(habbo)); + if(habbo != null && habbo.getClient() != null) { + habbo.getClient().sendResponse(new UserClubComposer(habbo, SubscriptionHabboClub.HABBO_CLUB, UserClubComposer.RESPONSE_TYPE_NORMAL)); habbo.getClient().sendResponse(new UserPermissionsComposer(habbo)); } } @@ -128,12 +144,15 @@ public class SubscriptionHabboClub extends Subscription { super.onExpired(); Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(this.getUserId()); - habbo.getHabboStats().maxFriends = Messenger.MAXIMUM_FRIENDS; - habbo.getHabboStats().maxRooms = RoomManager.MAXIMUM_ROOMS_USER; - Emulator.getThreading().run(habbo.getHabboStats()); + HabboInfo habboInfo = Emulator.getGameEnvironment().getHabboManager().getHabboInfo(this.getUserId()); + HabboStats stats = habboInfo.getHabboStats(); - if(ClothingValidationManager.VALIDATE_ON_HC_EXPIRE) { - habbo.getHabboInfo().setLook(ClothingValidationManager.validateLook(habbo, habbo.getHabboInfo().getLook(), habbo.getHabboInfo().getGender().name())); + stats.maxFriends = Messenger.MAXIMUM_FRIENDS; + stats.maxRooms = RoomManager.MAXIMUM_ROOMS_USER; + Emulator.getThreading().run(stats); + + if(habbo != null && ClothingValidationManager.VALIDATE_ON_HC_EXPIRE) { + habboInfo.setLook(ClothingValidationManager.validateLook(habbo, habboInfo.getLook(), habboInfo.getGender().name())); Emulator.getThreading().run(habbo.getHabboInfo()); if(habbo.getClient() != null) { @@ -145,8 +164,8 @@ public class SubscriptionHabboClub extends Subscription { } } - if(habbo.getClient() != null) { - habbo.getClient().sendResponse(new UserClubComposer(habbo)); + if(habbo != null && habbo.getClient() != null) { + habbo.getClient().sendResponse(new UserClubComposer(habbo, SubscriptionHabboClub.HABBO_CLUB, UserClubComposer.RESPONSE_TYPE_NORMAL)); habbo.getClient().sendResponse(new UserPermissionsComposer(habbo)); } } @@ -243,6 +262,7 @@ public class SubscriptionHabboClub extends Subscription { try { int userId = set.getInt("user_id"); HabboInfo habboInfo = Emulator.getGameEnvironment().getHabboManager().getHabboInfo(userId); + HabboStats stats = habboInfo.getHabboStats(); ClubCenterDataComposer calculated = calculatePayday(habboInfo); int totalReward = (calculated.creditRewardForMonthlySpent + calculated.creditRewardForStreakBonus); if(totalReward > 0) { @@ -250,7 +270,8 @@ public class SubscriptionHabboClub extends Subscription { HcPayDayLogEntry le = new HcPayDayLogEntry(timestampNow, userId, calculated.currentHcStreak, calculated.totalCreditsSpent, calculated.creditRewardForMonthlySpent, calculated.creditRewardForStreakBonus, totalReward, HC_PAYDAY_CURRENCY, claimed); Emulator.getThreading().run(le); } - habboInfo.getHabboStats().lastHCPayday = timestampNow; + stats.lastHCPayday = timestampNow; + Emulator.getThreading().run(stats); } catch (Exception e) { SubscriptionManager.LOGGER.error("Exception processing HC payday for user #" + set.getInt("user_id"), e); @@ -288,6 +309,13 @@ public class SubscriptionHabboClub extends Subscription { * @param habbo User to process */ public static void processUnclaimed(Habbo habbo) { + + progressAchievement(habbo.getHabboInfo()); + + if(habbo.getHabboStats().getRemainingClubGifts() > 0) { + habbo.getClient().sendResponse(new PickMonthlyClubGiftNotificationComposer(habbo.getHabboStats().getRemainingClubGifts())); + } + try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT * FROM `logs_hc_payday` WHERE user_id = ? AND claimed = 0")) { statement.setInt(1, habbo.getHabboInfo().getId()); @@ -368,4 +396,22 @@ public class SubscriptionHabboClub extends Subscription { return true; } + private static void progressAchievement(HabboInfo habboInfo) { + HabboStats stats = habboInfo.getHabboStats(); + Achievement achievement = Emulator.getGameEnvironment().getAchievementManager().getAchievement(ACHIEVEMENT_NAME); + if(achievement != null) { + int currentProgress = stats.getAchievementProgress(achievement); + if(currentProgress == -1) { + currentProgress = 0; + } + + int progressToSet = (int)Math.ceil(stats.getPastTimeAsClub() / 2678400.0); + int toIncrease = Math.max(progressToSet - currentProgress, 0); + + if(toIncrease > 0) { + AchievementManager.progressAchievement(habboInfo.getId(), achievement, toIncrease); + } + } + } + } diff --git a/src/main/java/com/eu/habbo/messages/PacketManager.java b/src/main/java/com/eu/habbo/messages/PacketManager.java index 36c6e2c9..00cdb987 100644 --- a/src/main/java/com/eu/habbo/messages/PacketManager.java +++ b/src/main/java/com/eu/habbo/messages/PacketManager.java @@ -255,6 +255,10 @@ public class PacketManager { this.registerHandler(Incoming.CatalogSearchedItemEvent, CatalogSearchedItemEvent.class); this.registerHandler(Incoming.PurchaseTargetOfferEvent, PurchaseTargetOfferEvent.class); this.registerHandler(Incoming.TargetOfferStateEvent, TargetOfferStateEvent.class); + this.registerHandler(Incoming.CatalogSelectClubGiftEvent, CatalogSelectClubGiftEvent.class); + this.registerHandler(Incoming.RequestClubCenterEvent, RequestClubCenterEvent.class); + this.registerHandler(Incoming.CatalogRequestClubDiscountEvent, CatalogRequestClubDiscountEvent.class); + this.registerHandler(Incoming.CatalogBuyClubDiscountEvent, CatalogBuyClubDiscountEvent.class); } private void registerEvent() throws Exception { diff --git a/src/main/java/com/eu/habbo/messages/incoming/Incoming.java b/src/main/java/com/eu/habbo/messages/incoming/Incoming.java index 2f2bfc1d..53a76eeb 100644 --- a/src/main/java/com/eu/habbo/messages/incoming/Incoming.java +++ b/src/main/java/com/eu/habbo/messages/incoming/Incoming.java @@ -40,6 +40,9 @@ public class Incoming { public static final int RequestHeightmapEvent = 3898; public static final int TradeCloseEvent = 2551; public static final int CatalogBuyItemEvent = 3492; + public static final int CatalogSelectClubGiftEvent = 2276; + public static final int CatalogRequestClubDiscountEvent = 2462; + public static final int CatalogBuyClubDiscountEvent = 3407; public static final int RequestGuildMembersEvent = 312; public static final int RequestPetInformationEvent = 2934; public static final int RoomUserWhisperEvent = 1543; @@ -72,6 +75,7 @@ public class Incoming { public static final int RequestPromotedRoomsEvent = 2908; public static final int GuildSetAdminEvent = 2894; public static final int GetClubDataEvent = 3285; + public static final int RequestClubCenterEvent = 869; public static final int RequestMeMenuSettingsEvent = 2388; public static final int MannequinSaveNameEvent = 2850; public static final int SellItemEvent = 3447; diff --git a/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogBuyClubDiscountEvent.java b/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogBuyClubDiscountEvent.java new file mode 100644 index 00000000..45567242 --- /dev/null +++ b/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogBuyClubDiscountEvent.java @@ -0,0 +1,88 @@ +package com.eu.habbo.messages.incoming.catalog; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.catalog.ClubOffer; +import com.eu.habbo.habbohotel.permissions.Permission; +import com.eu.habbo.habbohotel.users.subscriptions.Subscription; +import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionHabboClub; +import com.eu.habbo.messages.incoming.MessageHandler; +import com.eu.habbo.messages.outgoing.catalog.AlertPurchaseFailedComposer; +import com.eu.habbo.messages.outgoing.catalog.PurchaseOKComposer; +import com.eu.habbo.messages.outgoing.inventory.InventoryRefreshComposer; +import com.eu.habbo.messages.outgoing.unknown.ExtendClubMessageComposer; +import com.eu.habbo.messages.outgoing.users.UserClubComposer; +import com.eu.habbo.messages.outgoing.users.UserCreditsComposer; +import com.eu.habbo.messages.outgoing.users.UserCurrencyComposer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CatalogBuyClubDiscountEvent extends MessageHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(CatalogBuyClubDiscountEvent.class); + + @Override + public void handle() throws Exception { + + Subscription subscription = this.client.getHabbo().getHabboStats().getSubscription(SubscriptionHabboClub.HABBO_CLUB); + + int days = 0; + int minutes = 0; + int timeRemaining = 0; + + if(subscription != null) { + timeRemaining = subscription.getRemaining(); + days = (int) Math.floor(timeRemaining / 86400.0); + minutes = (int) Math.ceil(timeRemaining / 60.0); + + if(days < 1 && minutes > 0) { + days = 1; + } + } + + if(timeRemaining > 0 && SubscriptionHabboClub.DISCOUNT_ENABLED && days <= SubscriptionHabboClub.DISCOUNT_DAYS_BEFORE_END) { + ClubOffer deal = Emulator.getGameEnvironment().getCatalogManager().clubOffers.values().stream().filter(ClubOffer::isDeal).findAny().orElse(null); + + if(deal != null) { + ClubOffer regular = Emulator.getGameEnvironment().getCatalogManager().getClubOffers().stream().filter(x -> x.getDays() == deal.getDays()).findAny().orElse(null); + if(regular != null) { + + int totalDays = deal.getDays(); + int totalCredits = deal.getCredits(); + int totalDuckets = deal.getPoints(); + + if (totalDays > 0) { + if (this.client.getHabbo().getHabboInfo().getCurrencyAmount(deal.getPointsType()) < totalDuckets) + return; + + if (this.client.getHabbo().getHabboInfo().getCredits() < totalCredits) + return; + + if (!this.client.getHabbo().hasPermission(Permission.ACC_INFINITE_CREDITS)) + this.client.getHabbo().getHabboInfo().addCredits(-totalCredits); + + if (!this.client.getHabbo().hasPermission(Permission.ACC_INFINITE_POINTS)) + this.client.getHabbo().getHabboInfo().addCurrencyAmount(deal.getPointsType(), -totalDuckets); + + + if(this.client.getHabbo().getHabboStats().createSubscription(Subscription.HABBO_CLUB, (totalDays * 86400)) == null) { + this.client.sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR).compose()); + throw new Exception("Unable to create or extend subscription"); + } + + if (totalCredits > 0) + this.client.sendResponse(new UserCreditsComposer(this.client.getHabbo())); + + if (totalDuckets > 0) + this.client.sendResponse(new UserCurrencyComposer(this.client.getHabbo())); + + this.client.sendResponse(new PurchaseOKComposer(null)); + this.client.sendResponse(new InventoryRefreshComposer()); + + this.client.getHabbo().getHabboStats().run(); + } + } + } + } + + } +} diff --git a/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogBuyItemEvent.java b/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogBuyItemEvent.java index 17915caf..b0264ace 100644 --- a/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogBuyItemEvent.java +++ b/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogBuyItemEvent.java @@ -2,10 +2,7 @@ package com.eu.habbo.messages.incoming.catalog; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.bots.BotManager; -import com.eu.habbo.habbohotel.catalog.CatalogItem; -import com.eu.habbo.habbohotel.catalog.CatalogManager; -import com.eu.habbo.habbohotel.catalog.CatalogPage; -import com.eu.habbo.habbohotel.catalog.ClubOffer; +import com.eu.habbo.habbohotel.catalog.*; import com.eu.habbo.habbohotel.catalog.layouts.*; import com.eu.habbo.habbohotel.items.FurnitureType; import com.eu.habbo.habbohotel.permissions.Permission; @@ -66,10 +63,12 @@ public class CatalogBuyItemEvent extends MessageHandler { if (searchedItem.getOfferId() > 0) { page = Emulator.getGameEnvironment().getCatalogManager().getCatalogPage(searchedItem.getPageId()); - if (page.getCatalogItem(itemId).getOfferId() <= 0) { - page = null; - } else { - if (page.getRank() > this.client.getHabbo().getHabboInfo().getRank().getId()) { + if(page != null) { + if (page.getCatalogItem(itemId).getOfferId() <= 0) { + page = null; + } else if (page.getRank() > this.client.getHabbo().getHabboInfo().getRank().getId()) { + page = null; + } else if (page.getLayout() != null && page.getLayout().equalsIgnoreCase(CatalogPageLayouts.club_gift.name())) { page = null; } } @@ -77,6 +76,10 @@ public class CatalogBuyItemEvent extends MessageHandler { } else { page = Emulator.getGameEnvironment().getCatalogManager().catalogPages.get(pageId); + if(page != null && page.getLayout() != null && page.getLayout().equalsIgnoreCase(CatalogPageLayouts.club_gift.name())) { + page = null; + } + if (page instanceof RoomBundleLayout) { final CatalogItem[] item = new CatalogItem[1]; page.getCatalogItems().forEachValue(new TObjectProcedure() { diff --git a/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogRequestClubDiscountEvent.java b/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogRequestClubDiscountEvent.java new file mode 100644 index 00000000..1d5bc5c6 --- /dev/null +++ b/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogRequestClubDiscountEvent.java @@ -0,0 +1,54 @@ +package com.eu.habbo.messages.incoming.catalog; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.catalog.CatalogItem; +import com.eu.habbo.habbohotel.catalog.CatalogPage; +import com.eu.habbo.habbohotel.catalog.CatalogPageLayouts; +import com.eu.habbo.habbohotel.catalog.ClubOffer; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.users.subscriptions.Subscription; +import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionHabboClub; +import com.eu.habbo.messages.incoming.MessageHandler; +import com.eu.habbo.messages.outgoing.catalog.AlertPurchaseFailedComposer; +import com.eu.habbo.messages.outgoing.unknown.ExtendClubMessageComposer; +import com.eu.habbo.messages.outgoing.users.ClubGiftReceivedComposer; +import gnu.trove.set.hash.THashSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CatalogRequestClubDiscountEvent extends MessageHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(CatalogRequestClubDiscountEvent.class); + + @Override + public void handle() throws Exception { + + Subscription subscription = this.client.getHabbo().getHabboStats().getSubscription(SubscriptionHabboClub.HABBO_CLUB); + + int days = 0; + int minutes = 0; + int timeRemaining = 0; + + if(subscription != null) { + timeRemaining = subscription.getRemaining(); + days = (int) Math.floor(timeRemaining / 86400.0); + minutes = (int) Math.ceil(timeRemaining / 60.0); + + if(days < 1 && minutes > 0) { + days = 1; + } + } + + if(timeRemaining > 0 && SubscriptionHabboClub.DISCOUNT_ENABLED && days <= SubscriptionHabboClub.DISCOUNT_DAYS_BEFORE_END) { + ClubOffer deal = Emulator.getGameEnvironment().getCatalogManager().clubOffers.values().stream().filter(ClubOffer::isDeal).findAny().orElse(null); + + if(deal != null) { + ClubOffer regular = Emulator.getGameEnvironment().getCatalogManager().getClubOffers().stream().filter(x -> x.getDays() == deal.getDays()).findAny().orElse(null); + if(regular != null) { + this.client.sendResponse(new ExtendClubMessageComposer(this.client.getHabbo(), deal, regular.getCredits(), regular.getPoints(), regular.getPointsType(), Math.max(0, days))); + } + } + } + + } +} diff --git a/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogSelectClubGiftEvent.java b/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogSelectClubGiftEvent.java new file mode 100644 index 00000000..e4d6794d --- /dev/null +++ b/src/main/java/com/eu/habbo/messages/incoming/catalog/CatalogSelectClubGiftEvent.java @@ -0,0 +1,77 @@ +package com.eu.habbo.messages.incoming.catalog; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.catalog.CatalogItem; +import com.eu.habbo.habbohotel.catalog.CatalogPage; +import com.eu.habbo.habbohotel.catalog.CatalogPageLayouts; +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.messages.incoming.MessageHandler; +import com.eu.habbo.messages.outgoing.catalog.*; +import com.eu.habbo.messages.outgoing.users.ClubGiftReceivedComposer; +import gnu.trove.set.hash.THashSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CatalogSelectClubGiftEvent extends MessageHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(CatalogSelectClubGiftEvent.class); + + @Override + public void handle() throws Exception { + + String itemName = this.packet.readString(); + + if(itemName.isEmpty()) { + LOGGER.error("itemName is empty"); + this.client.sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR)); + return; + } + + if(this.client.getHabbo().getHabboStats().getRemainingClubGifts() < 1) { + LOGGER.error("User has no remaining club gifts"); + this.client.sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR)); + return; + } + + CatalogPage page = Emulator.getGameEnvironment().getCatalogManager().getCatalogPageByLayout(CatalogPageLayouts.club_gift.name().toLowerCase()); + + if(page == null) { + LOGGER.error("Catalog page not found"); + this.client.sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR)); + return; + } + + CatalogItem catalogItem = page.getCatalogItems().valueCollection().stream().filter(x -> x.getName().equalsIgnoreCase(itemName)).findAny().orElse(null); + + if(catalogItem == null) { + LOGGER.error("Catalog item not found"); + this.client.sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR)); + return; + } + + int daysRequired = 0; + try { + daysRequired = Integer.parseInt(catalogItem.getExtradata()); + } + catch (NumberFormatException ignored) { } + + if(daysRequired > (int) Math.floor(this.client.getHabbo().getHabboStats().getPastTimeAsClub() / 86400.0)) { + LOGGER.error("Not been member for long enough"); + this.client.sendResponse(new AlertPurchaseFailedComposer(AlertPurchaseFailedComposer.SERVER_ERROR)); + return; + } + + THashSet itemsGiven = new THashSet<>(); + for(Item item : catalogItem.getBaseItems()) { + if(Emulator.getGameEnvironment().getItemManager().createGift(this.client.getHabbo().getHabboInfo().getId(), item, "", 0, 0) != null) { + itemsGiven.add(item); + } + } + + this.client.getHabbo().getHabboStats().hcGiftsClaimed++; + Emulator.getThreading().run(this.client.getHabbo().getHabboStats()); + + this.client.sendResponse(new ClubGiftReceivedComposer(itemName, itemsGiven)); + + } +} diff --git a/src/main/java/com/eu/habbo/messages/incoming/catalog/RequestClubDataEvent.java b/src/main/java/com/eu/habbo/messages/incoming/catalog/RequestClubDataEvent.java index 79eb7525..6085e1eb 100644 --- a/src/main/java/com/eu/habbo/messages/incoming/catalog/RequestClubDataEvent.java +++ b/src/main/java/com/eu/habbo/messages/incoming/catalog/RequestClubDataEvent.java @@ -9,6 +9,5 @@ public class RequestClubDataEvent extends MessageHandler { @Override public void handle() throws Exception { this.client.sendResponse(new ClubDataComposer(this.client.getHabbo(), this.packet.readInt())); - this.client.sendResponse(SubscriptionHabboClub.calculatePayday(this.client.getHabbo().getHabboInfo())); } } diff --git a/src/main/java/com/eu/habbo/messages/incoming/catalog/RequestClubGiftsEvent.java b/src/main/java/com/eu/habbo/messages/incoming/catalog/RequestClubGiftsEvent.java index 98bc8014..777e65fd 100644 --- a/src/main/java/com/eu/habbo/messages/incoming/catalog/RequestClubGiftsEvent.java +++ b/src/main/java/com/eu/habbo/messages/incoming/catalog/RequestClubGiftsEvent.java @@ -6,6 +6,10 @@ import com.eu.habbo.messages.outgoing.catalog.ClubGiftsComposer; public class RequestClubGiftsEvent extends MessageHandler { @Override public void handle() throws Exception { - this.client.sendResponse(new ClubGiftsComposer()); + this.client.sendResponse(new ClubGiftsComposer( + (int) Math.floor(this.client.getHabbo().getHabboStats().getTimeTillNextClubGift() / 86400.0), + this.client.getHabbo().getHabboStats().getRemainingClubGifts(), + (int) Math.floor(this.client.getHabbo().getHabboStats().getPastTimeAsClub() / 86400.0) + )); } } diff --git a/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java b/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java index f28c9758..89ca6250 100644 --- a/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java +++ b/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java @@ -138,7 +138,7 @@ public class SecureLoginEvent extends MessageHandler { //messages.add(new MessengerInitComposer(this.client.getHabbo()).compose()); //messages.add(new FriendsComposer(this.client.getHabbo()).compose()); - messages.add(new UserClubComposer(this.client.getHabbo()).compose()); + messages.add(new UserClubComposer(this.client.getHabbo(), SubscriptionHabboClub.HABBO_CLUB, UserClubComposer.RESPONSE_TYPE_LOGIN).compose()); if (this.client.getHabbo().hasPermission(Permission.ACC_SUPPORTTOOL)) { messages.add(new ModToolComposer(this.client.getHabbo()).compose()); diff --git a/src/main/java/com/eu/habbo/messages/incoming/users/RequestClubCenterEvent.java b/src/main/java/com/eu/habbo/messages/incoming/users/RequestClubCenterEvent.java new file mode 100644 index 00000000..80dede05 --- /dev/null +++ b/src/main/java/com/eu/habbo/messages/incoming/users/RequestClubCenterEvent.java @@ -0,0 +1,12 @@ +package com.eu.habbo.messages.incoming.users; + +import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionHabboClub; +import com.eu.habbo.messages.incoming.MessageHandler; +import com.eu.habbo.messages.outgoing.users.UserClubComposer; + +public class RequestClubCenterEvent extends MessageHandler { + @Override + public void handle() throws Exception { + this.client.sendResponse(SubscriptionHabboClub.calculatePayday(this.client.getHabbo().getHabboInfo())); + } +} diff --git a/src/main/java/com/eu/habbo/messages/incoming/users/RequestUserClubEvent.java b/src/main/java/com/eu/habbo/messages/incoming/users/RequestUserClubEvent.java index c560fbb1..34fb4905 100644 --- a/src/main/java/com/eu/habbo/messages/incoming/users/RequestUserClubEvent.java +++ b/src/main/java/com/eu/habbo/messages/incoming/users/RequestUserClubEvent.java @@ -6,6 +6,7 @@ import com.eu.habbo.messages.outgoing.users.UserClubComposer; public class RequestUserClubEvent extends MessageHandler { @Override public void handle() throws Exception { - this.client.sendResponse(new UserClubComposer(this.client.getHabbo())); + String subscriptionType = this.packet.readString(); + this.client.sendResponse(new UserClubComposer(this.client.getHabbo(), subscriptionType)); } } diff --git a/src/main/java/com/eu/habbo/messages/outgoing/catalog/ClubDataComposer.java b/src/main/java/com/eu/habbo/messages/outgoing/catalog/ClubDataComposer.java index 6d44e75d..96188ad2 100644 --- a/src/main/java/com/eu/habbo/messages/outgoing/catalog/ClubDataComposer.java +++ b/src/main/java/com/eu/habbo/messages/outgoing/catalog/ClubDataComposer.java @@ -28,45 +28,7 @@ public class ClubDataComposer extends MessageComposer { //TODO Change this to a seperate table. for (ClubOffer offer : offers) { - this.response.appendInt(offer.getId()); - this.response.appendString(offer.getName()); - this.response.appendBoolean(false); //unused - this.response.appendInt(offer.getCredits()); - this.response.appendInt(offer.getPoints()); - this.response.appendInt(offer.getPointsType()); - this.response.appendBoolean(offer.isVip()); - - long seconds = offer.getDays() * 86400L; - - long secondsTotal = seconds; - - int totalYears = (int) Math.floor((int) seconds / 86400 * 31 * 12); - seconds -= totalYears * 86400 * 31 * 12; - - int totalMonths = (int) Math.floor((int) seconds / 86400 * 31); - seconds -= totalMonths * 86400 * 31; - - int totalDays = (int) Math.floor((int) seconds / 86400); - seconds -= totalDays * 86400; - - this.response.appendInt((int) secondsTotal / 86400 / 31); - this.response.appendInt((int) seconds); - this.response.appendBoolean(false); //giftable - this.response.appendInt((int) seconds); - - int endTimestamp = this.habbo.getHabboStats().getClubExpireTimestamp(); - - if (endTimestamp < Emulator.getIntUnixTimestamp()) { - endTimestamp = Emulator.getIntUnixTimestamp(); - } - - endTimestamp += secondsTotal; - - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(endTimestamp * 1000L); - this.response.appendInt(cal.get(Calendar.YEAR)); - this.response.appendInt(cal.get(Calendar.MONTH) + 1); - this.response.appendInt(cal.get(Calendar.DAY_OF_MONTH)); + offer.serialize(this.response, this.habbo.getHabboStats().getClubExpireTimestamp()); } this.response.appendInt(this.windowId); diff --git a/src/main/java/com/eu/habbo/messages/outgoing/catalog/ClubGiftsComposer.java b/src/main/java/com/eu/habbo/messages/outgoing/catalog/ClubGiftsComposer.java index f4e1161d..13e4bfc6 100644 --- a/src/main/java/com/eu/habbo/messages/outgoing/catalog/ClubGiftsComposer.java +++ b/src/main/java/com/eu/habbo/messages/outgoing/catalog/ClubGiftsComposer.java @@ -3,63 +3,60 @@ package com.eu.habbo.messages.outgoing.catalog; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.catalog.CatalogItem; import com.eu.habbo.habbohotel.catalog.CatalogPage; +import com.eu.habbo.habbohotel.catalog.CatalogPageLayouts; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.outgoing.MessageComposer; import com.eu.habbo.messages.outgoing.Outgoing; import gnu.trove.iterator.TIntObjectIterator; +import gnu.trove.procedure.TObjectProcedure; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.NoSuchElementException; public class ClubGiftsComposer extends MessageComposer { + + private final int daysTillNextGift; + private final int availableGifts; + private final int daysAsHc; + + public ClubGiftsComposer(int daysTillNextGift, int availableGifts, int daysAsHc) { + this.daysTillNextGift = daysTillNextGift; + this.availableGifts = availableGifts; + this.daysAsHc = daysAsHc; + } + @Override protected ServerMessage composeInternal() { this.response.init(Outgoing.ClubGiftsComposer); - this.response.appendInt(0); //Days Until Next Gift - this.response.appendInt(1); //Gift Selectable + this.response.appendInt(this.daysTillNextGift); //Days Until Next Gift + this.response.appendInt(this.availableGifts); //Gift Selectable - CatalogPage page = Emulator.getGameEnvironment().getCatalogManager().getCatalogPage(Emulator.getConfig().getInt("catalog.page.vipgifts")); + CatalogPage page = Emulator.getGameEnvironment().getCatalogManager().getCatalogPageByLayout(CatalogPageLayouts.club_gift.name().toLowerCase()); if (page != null) { - this.response.appendInt(page.getCatalogItems().size()); + final List items = new ArrayList<>(page.getCatalogItems().valueCollection()); + Collections.sort(items); - TIntObjectIterator iterator = page.getCatalogItems().iterator(); - for (int i = page.getCatalogItems().size(); i-- > 0; ) { - try { - iterator.advance(); - - CatalogItem item = iterator.value(); - - if (item != null) { - item.serialize(this.response); - } - } catch (NoSuchElementException e) { - break; - } + this.response.appendInt(items.size()); + for(CatalogItem item : items) { + item.serialize(this.response); } - this.response.appendInt(page.getCatalogItems().size()); - iterator = page.getCatalogItems().iterator(); - for (int i = page.getCatalogItems().size(); i-- > 0; ) { + this.response.appendInt(items.size()); + for(CatalogItem item : items) { + int daysRequired = 0; try { - iterator.advance(); - - CatalogItem item = iterator.value(); - - if (item != null) { - this.response.appendInt(item.getId()); - this.response.appendBoolean(true); - this.response.appendInt(i); - this.response.appendBoolean(true); - } else { - this.response.appendInt(-100); - this.response.appendBoolean(false); - this.response.appendInt(-100); - this.response.appendBoolean(false); - } - } catch (NoSuchElementException e) { - break; + daysRequired = Integer.parseInt(item.getExtradata()); } + catch (NumberFormatException ignored) { } + + this.response.appendInt(item.getId()); + this.response.appendBoolean(item.isClubOnly()); + this.response.appendInt(Math.max(daysRequired - daysAsHc, 0)); + this.response.appendBoolean(daysRequired <= daysAsHc); } } else { this.response.appendInt(0); diff --git a/src/main/java/com/eu/habbo/messages/outgoing/unknown/ExtendClubMessageComposer.java b/src/main/java/com/eu/habbo/messages/outgoing/unknown/ExtendClubMessageComposer.java index ec7cf3a1..8225c519 100644 --- a/src/main/java/com/eu/habbo/messages/outgoing/unknown/ExtendClubMessageComposer.java +++ b/src/main/java/com/eu/habbo/messages/outgoing/unknown/ExtendClubMessageComposer.java @@ -1,88 +1,37 @@ package com.eu.habbo.messages.outgoing.unknown; -import com.eu.habbo.Emulator; -import com.eu.habbo.habbohotel.catalog.CatalogItem; +import com.eu.habbo.habbohotel.catalog.ClubOffer; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.outgoing.MessageComposer; import com.eu.habbo.messages.outgoing.Outgoing; -import java.util.Calendar; - public class ExtendClubMessageComposer extends MessageComposer { private final Habbo habbo; - private final CatalogItem item; - private final int unknownInt1; - private final int unknownInt2; - private final int unknownInt3; - private final int unknownInt4; + private final ClubOffer offer; + private final int normalCreditCost; + private final int normalPointsCost; + private final int pointsType; + private final int daysRemaining; - public ExtendClubMessageComposer(Habbo habbo, CatalogItem item, int unknownInt1, int unknownInt2, int unknownInt3, int unknownInt4) { + public ExtendClubMessageComposer(Habbo habbo, ClubOffer offer, int normalCreditCost, int normalPointsCost, int pointsType, int daysRemaining) { this.habbo = habbo; - this.item = item; - this.unknownInt1 = unknownInt1; - this.unknownInt2 = unknownInt2; - this.unknownInt3 = unknownInt3; - this.unknownInt4 = unknownInt4; + this.offer = offer; + this.normalCreditCost = normalCreditCost; + this.normalPointsCost = normalPointsCost; + this.pointsType = pointsType; + this.daysRemaining = daysRemaining; } @Override protected ServerMessage composeInternal() { this.response.init(Outgoing.ExtendClubMessageComposer); - this.response.appendInt(this.item.getId()); - this.response.appendString(this.item.getName()); - this.response.appendBoolean(false); //unused - this.response.appendInt(this.item.getCredits()); - this.response.appendInt(this.item.getPoints()); - this.response.appendInt(this.item.getPointsType()); - this.response.appendBoolean(this.item.getName().contains("_VIP_")); + this.offer.serialize(this.response, this.habbo.getHabboStats().getClubExpireTimestamp()); - String[] data = this.item.getName().replace("_VIP_", "_").toLowerCase().split("_"); - - long seconds = 0; - - if (data[3].toLowerCase().startsWith("day")) { - seconds = 86400 * Integer.valueOf(data[2]); - } else if (data[3].toLowerCase().startsWith("month")) { - seconds = 86400 * 31 * Integer.valueOf(data[2]); - } else if (data[3].toLowerCase().startsWith("year")) { - seconds = 86400 * 31 * 12 * Integer.valueOf(data[2]); - } - - long secondsTotal = seconds; - - int totalYears = (int) Math.floor((int) seconds / 86400 * 31 * 12); - seconds -= totalYears * 86400 * 31 * 12; - - int totalMonths = (int) Math.floor((int) seconds / 86400 * 31); - seconds -= totalMonths * 86400 * 31; - - int totalDays = (int) Math.floor((int) seconds / 86400); - seconds -= totalDays * 86400; - - this.response.appendInt((int) secondsTotal / 86400 / 31); - this.response.appendInt((int) seconds); - this.response.appendBoolean(false); //giftable - this.response.appendInt((int) seconds); - - int endTimestamp = this.habbo.getHabboStats().getClubExpireTimestamp(); - - if (endTimestamp < Emulator.getIntUnixTimestamp()) { - endTimestamp = Emulator.getIntUnixTimestamp(); - } - - endTimestamp += secondsTotal; - - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(endTimestamp * 1000L); - this.response.appendInt(cal.get(Calendar.YEAR)); - this.response.appendInt(cal.get(Calendar.MONTH) + 1); - this.response.appendInt(cal.get(Calendar.DAY_OF_MONTH)); - - this.response.appendInt(this.unknownInt1); - this.response.appendInt(this.unknownInt2); - this.response.appendInt(this.unknownInt3); - this.response.appendInt(this.unknownInt4); + this.response.appendInt(this.normalCreditCost); + this.response.appendInt(this.normalPointsCost); + this.response.appendInt(this.pointsType); + this.response.appendInt(this.daysRemaining); return this.response; } } \ No newline at end of file diff --git a/src/main/java/com/eu/habbo/messages/outgoing/users/ClubGiftReceivedComposer.java b/src/main/java/com/eu/habbo/messages/outgoing/users/ClubGiftReceivedComposer.java index b67608bd..de0755f4 100644 --- a/src/main/java/com/eu/habbo/messages/outgoing/users/ClubGiftReceivedComposer.java +++ b/src/main/java/com/eu/habbo/messages/outgoing/users/ClubGiftReceivedComposer.java @@ -1,6 +1,6 @@ package com.eu.habbo.messages.outgoing.users; -import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.outgoing.MessageComposer; import com.eu.habbo.messages.outgoing.Outgoing; @@ -8,23 +8,23 @@ import gnu.trove.set.hash.THashSet; public class ClubGiftReceivedComposer extends MessageComposer { //:test 735 s:t i:1 s:s i:230 s:throne i:1 b:1 i:1 i:10; - private final THashSet items; + private final String name; + private final THashSet items; - public ClubGiftReceivedComposer(THashSet items) { + public ClubGiftReceivedComposer(String name, THashSet items) { + this.name = name; this.items = items; } @Override protected ServerMessage composeInternal() { this.response.init(Outgoing.ClubGiftReceivedComposer); + + this.response.appendString(this.name); this.response.appendInt(this.items.size()); - for (HabboItem item : this.items) { - this.response.appendString(item.getBaseItem().getType().code); - this.response.appendInt(item.getBaseItem().getId()); - this.response.appendString(item.getBaseItem().getName()); - this.response.appendInt(0); - this.response.appendBoolean(false); + for (Item item : this.items) { + item.serialize(this.response); } return this.response; diff --git a/src/main/java/com/eu/habbo/messages/outgoing/users/UserClubComposer.java b/src/main/java/com/eu/habbo/messages/outgoing/users/UserClubComposer.java index 6bcb5f65..695561d2 100644 --- a/src/main/java/com/eu/habbo/messages/outgoing/users/UserClubComposer.java +++ b/src/main/java/com/eu/habbo/messages/outgoing/users/UserClubComposer.java @@ -3,6 +3,8 @@ package com.eu.habbo.messages.outgoing.users; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.subscriptions.Subscription; +import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionHabboClub; +import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionManager; import com.eu.habbo.messages.ServerMessage; import com.eu.habbo.messages.outgoing.MessageComposer; import com.eu.habbo.messages.outgoing.Outgoing; @@ -13,22 +15,59 @@ import java.util.concurrent.TimeUnit; public class UserClubComposer extends MessageComposer { private final Habbo habbo; + private final String subscriptionType; + private final int responseType; + + public static int RESPONSE_TYPE_NORMAL = 0; + public static int RESPONSE_TYPE_LOGIN = 1; + public static int RESPONSE_TYPE_PURCHASE = 2; // closes the catalog after buying + public static int RESPONSE_TYPE_DISCOUNT_AVAILABLE = 3; + public static int RESPONSE_TYPE_CITIZENSHIP_DISCOUNT = 4; public UserClubComposer(Habbo habbo) { this.habbo = habbo; + this.subscriptionType = SubscriptionHabboClub.HABBO_CLUB.toLowerCase(); + this.responseType = 0; + } + + public UserClubComposer(Habbo habbo, String subscriptionType) { + this.habbo = habbo; + this.subscriptionType = subscriptionType; + this.responseType = 0; + } + + public UserClubComposer(Habbo habbo, String subscriptionType, int responseType) { + this.habbo = habbo; + this.subscriptionType = subscriptionType; + this.responseType = responseType; } @Override protected ServerMessage composeInternal() { this.response.init(Outgoing.UserClubComposer); - this.response.appendString("club_habbo"); + this.response.appendString(this.subscriptionType.toLowerCase()); - Subscription subscription = this.habbo.getHabboStats().getSubscription(Subscription.HABBO_CLUB); + if(Emulator.getGameEnvironment().getSubscriptionManager().getSubscriptionClass(this.subscriptionType.toUpperCase()) == null) { + this.response.appendInt(0); // daysToPeriodEnd + this.response.appendInt(0); // memberPeriods + this.response.appendInt(0); // periodsSubscribedAhead + this.response.appendInt(0); // responseType + this.response.appendBoolean(false); // hasEverBeenMember + this.response.appendBoolean(false); // isVIP + this.response.appendInt(0); // pastClubDays + this.response.appendInt(0); // pastVIPdays + this.response.appendInt(0); // minutesTillExpiration + this.response.appendInt(0); // minutesSinceLastModified + return this.response; + } + + Subscription subscription = this.habbo.getHabboStats().getSubscription(this.subscriptionType); int days = 0; int minutes = 0; int timeRemaining = 0; + int pastTimeAsHC = this.habbo.getHabboStats().getPastTimeAsClub(); if(subscription != null) { timeRemaining = subscription.getRemaining(); @@ -40,14 +79,16 @@ public class UserClubComposer extends MessageComposer { } } + int responseType = ((this.responseType <= RESPONSE_TYPE_LOGIN) && timeRemaining > 0 && SubscriptionHabboClub.DISCOUNT_ENABLED && days <= SubscriptionHabboClub.DISCOUNT_DAYS_BEFORE_END) ? RESPONSE_TYPE_DISCOUNT_AVAILABLE : this.responseType; + this.response.appendInt(days); // daysToPeriodEnd this.response.appendInt(0); // memberPeriods this.response.appendInt(0); // periodsSubscribedAhead - this.response.appendInt(0); // responseType - this.response.appendBoolean(true); // hasEverBeenMember + this.response.appendInt(responseType); // responseType + this.response.appendBoolean(pastTimeAsHC > 0); // hasEverBeenMember this.response.appendBoolean(true); // isVIP this.response.appendInt(0); // pastClubDays - this.response.appendInt(0); // pastVIPdays + this.response.appendInt((int) Math.floor(pastTimeAsHC / 86400.0)); // pastVIPdays this.response.appendInt(minutes); // minutesTillExpiration this.response.appendInt((Emulator.getIntUnixTimestamp() - this.habbo.getHabboStats().hcMessageLastModified) / 60); // minutesSinceLastModified this.habbo.getHabboStats().hcMessageLastModified = Emulator.getIntUnixTimestamp(); @@ -63,6 +104,14 @@ public class UserClubComposer extends MessageComposer { // int - minutesTillExpiration // (optional) int - minutesSinceLastModified + /* + responseType: + 1 = RESPONSE_TYPE_LOGIN + 2 = RESPONSE_TYPE_PURCHASE + 3 = RESPONSE_TYPE_DISCOUNT_AVAILABLE + 4 = RESPONSE_TYPE_CITIZENSHIP_DISCOUNT + */ + /* int endTimestamp = this.habbo.getHabboStats().getClubExpireTimestamp(); diff --git a/src/main/java/com/eu/habbo/messages/rcon/ModifyUserSubscription.java b/src/main/java/com/eu/habbo/messages/rcon/ModifyUserSubscription.java new file mode 100644 index 00000000..8ec3e2ea --- /dev/null +++ b/src/main/java/com/eu/habbo/messages/rcon/ModifyUserSubscription.java @@ -0,0 +1,108 @@ +package com.eu.habbo.messages.rcon; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.rooms.RoomChatMessageBubbles; +import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.habbohotel.users.HabboInfo; +import com.eu.habbo.habbohotel.users.subscriptions.Subscription; +import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDataComposer; +import com.eu.habbo.messages.outgoing.users.MeMenuSettingsComposer; +import com.eu.habbo.messages.outgoing.users.UpdateUserLookComposer; +import com.google.gson.Gson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public class ModifyUserSubscription extends RCONMessage { + private static final Logger LOGGER = LoggerFactory.getLogger(ModifyUserSubscription.class); + + public ModifyUserSubscription() { + super(ModifyUserSubscription.JSON.class); + } + + @Override + public void handle(Gson gson, JSON json) { + try { + + if(json.user_id <= 0) { + this.status = RCONMessage.HABBO_NOT_FOUND; + this.message = "User not found"; + return; + } + + if (!Emulator.getGameEnvironment().getSubscriptionManager().types.containsKey(json.type)) { + this.status = RCONMessage.STATUS_ERROR; + this.message = "%subscription% is not a valid subscription type".replace("%subscription%", json.type); + return; + } + + HabboInfo habbo = Emulator.getGameEnvironment().getHabboManager().getHabboInfo(json.user_id); + + if (habbo == null) { + this.status = RCONMessage.HABBO_NOT_FOUND; + this.message = "User not found"; + return; + } + + if (json.action.equalsIgnoreCase("add") || json.action.equalsIgnoreCase("+") || json.action.equalsIgnoreCase("a")) { + if (json.duration < 1) { + this.status = RCONMessage.STATUS_ERROR; + this.message = "duration must be > 0"; + return; + } + + habbo.getHabboStats().createSubscription(json.type, json.duration); + this.status = RCONMessage.STATUS_OK; + this.message = "Successfully added %time% seconds to %subscription% on %user%".replace("%time%", json.duration + "").replace("%user%", habbo.getUsername()).replace("%subscription%", json.type); + } else if (json.action.equalsIgnoreCase("remove") || json.action.equalsIgnoreCase("-") || json.action.equalsIgnoreCase("r")) { + Subscription s = habbo.getHabboStats().getSubscription(json.type); + + if (s == null) { + this.status = RCONMessage.STATUS_ERROR; + this.message = "%user% does not have the %subscription% subscription".replace("%user%", habbo.getUsername()).replace("%subscription%", json.type); + return; + } + + if (json.duration != -1) { + if (json.duration < 1) { + this.status = RCONMessage.STATUS_ERROR; + this.message = "duration must be > 0 or -1 to remove all time"; + return; + } + + s.addDuration(-json.duration); + this.status = RCONMessage.STATUS_OK; + this.message = "Successfully removed %time% seconds from %subscription% on %user%".replace("%time%", json.duration + "").replace("%user%", habbo.getUsername()).replace("%subscription%", json.type); + } else { + s.addDuration(-s.getRemaining()); + this.status = RCONMessage.STATUS_OK; + this.message = "Successfully removed %subscription% sub from %user%".replace("%user%", habbo.getUsername()).replace("%subscription%", json.type); + } + } + else { + this.status = RCONMessage.STATUS_ERROR; + this.message = "Invalid action specified. Must be add, +, remove or -"; + } + } + catch (Exception e) { + this.status = RCONMessage.SYSTEM_ERROR; + this.message = "Exception occurred"; + LOGGER.error("Exception occurred", e); + } + } + + static class JSON { + + public int user_id; + + public String type = ""; // Subscription type e.g. HABBO_CLUB + + public String action = ""; // Can be add or remove + + public int duration = -1; // Time to add/remove in seconds. -1 means remove subscription entirely + + } +} \ No newline at end of file diff --git a/src/main/java/com/eu/habbo/networking/rconserver/RCONServer.java b/src/main/java/com/eu/habbo/networking/rconserver/RCONServer.java index ebc225f5..7bbd9f05 100644 --- a/src/main/java/com/eu/habbo/networking/rconserver/RCONServer.java +++ b/src/main/java/com/eu/habbo/networking/rconserver/RCONServer.java @@ -61,6 +61,7 @@ public class RCONServer extends Server { this.addRCONMessage("ignoreuser", IgnoreUser.class); this.addRCONMessage("setmotto", SetMotto.class); this.addRCONMessage("giveuserclothing", GiveUserClothing.class); + this.addRCONMessage("modifysubscription", ModifyUserSubscription.class); Collections.addAll(this.allowedAdresses, Emulator.getConfig().getValue("rcon.allowed", "127.0.0.1").split(";")); } diff --git a/src/main/java/com/eu/habbo/plugin/PluginManager.java b/src/main/java/com/eu/habbo/plugin/PluginManager.java index 041f835d..eff47754 100644 --- a/src/main/java/com/eu/habbo/plugin/PluginManager.java +++ b/src/main/java/com/eu/habbo/plugin/PluginManager.java @@ -178,6 +178,9 @@ public class PluginManager { SubscriptionHabboClub.HC_PAYDAY_CURRENCY = Emulator.getConfig().getValue("subscriptions.hc.payday.currency"); SubscriptionHabboClub.HC_PAYDAY_KICKBACK_PERCENTAGE = Emulator.getConfig().getInt("subscriptions.hc.payday.percentage", 10) / 100.0; SubscriptionHabboClub.HC_PAYDAY_COINSSPENT_RESET_ON_EXPIRE = Emulator.getConfig().getBoolean("subscriptions.hc.payday.creditsspent_reset_on_expire", false); + SubscriptionHabboClub.ACHIEVEMENT_NAME = Emulator.getConfig().getValue("subscriptions.hc.achievement", "VipHC"); + SubscriptionHabboClub.DISCOUNT_ENABLED = Emulator.getConfig().getBoolean("subscriptions.hc.discount.enabled", false); + SubscriptionHabboClub.DISCOUNT_DAYS_BEFORE_END = Emulator.getConfig().getInt("subscriptions.hc.discount.days_before_end", 7); SubscriptionHabboClub.HC_PAYDAY_STREAK.clear(); for (String streak : Emulator.getConfig().getValue("subscriptions.hc.payday.streak", "7=5;30=10;60=15;90=20;180=25;365=30").split(Pattern.quote(";"))) { From 0b4a5b000f28a860b30c049e7212f2b7f39a502a Mon Sep 17 00:00:00 2001 From: Beny Date: Tue, 6 Oct 2020 00:28:45 +0200 Subject: [PATCH 3/5] Better error handling in figureData parser --- .../users/clothingvalidation/Figuredata.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/Figuredata.java b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/Figuredata.java index 11eadc9f..c68eedc5 100644 --- a/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/Figuredata.java +++ b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/Figuredata.java @@ -29,7 +29,7 @@ public class Figuredata { * @throws IOException * @throws SAXException */ - public void parseXML(String uri) throws ParserConfigurationException, IOException, SAXException { + public void parseXML(String uri) throws Exception, ParserConfigurationException, IOException, SAXException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(false); factory.setIgnoringElementContentWhitespace(true); @@ -37,6 +37,15 @@ public class Figuredata { Document document = builder.parse(uri); Element rootElement = document.getDocumentElement(); + + if(!rootElement.getTagName().equalsIgnoreCase("figuredata")) { + throw new Exception("The passed file is not in figuredata format. Received " + document.toString()); + } + + if(document.getElementsByTagName("colors") == null || document.getElementsByTagName("sets") == null) { + throw new Exception("The passed file is not in figuredata format. Received " + document.toString()); + } + NodeList palettesList = document.getElementsByTagName("colors").item(0).getChildNodes(); NodeList settypesList = document.getElementsByTagName("sets").item(0).getChildNodes(); From b75225e707e433999502cd59a879570bf196606e Mon Sep 17 00:00:00 2001 From: Beny Date: Tue, 6 Oct 2020 00:47:15 +0200 Subject: [PATCH 4/5] Fixed wrong Valid until date in catalog for non hc --- .../eu/habbo/habbohotel/catalog/ClubOffer.java | 2 +- .../users/clothingvalidation/Figuredata.java | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/eu/habbo/habbohotel/catalog/ClubOffer.java b/src/main/java/com/eu/habbo/habbohotel/catalog/ClubOffer.java index de40e8ef..e615e13d 100644 --- a/src/main/java/com/eu/habbo/habbohotel/catalog/ClubOffer.java +++ b/src/main/java/com/eu/habbo/habbohotel/catalog/ClubOffer.java @@ -82,7 +82,7 @@ public class ClubOffer implements ISerialize { } public void serialize(ServerMessage message, int hcExpireTimestamp) { - hcExpireTimestamp = Math.min(Emulator.getIntUnixTimestamp(), hcExpireTimestamp); + hcExpireTimestamp = Math.max(Emulator.getIntUnixTimestamp(), hcExpireTimestamp); message.appendInt(this.id); message.appendString(this.name); message.appendBoolean(false); //unused diff --git a/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/Figuredata.java b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/Figuredata.java index c68eedc5..c8c67671 100644 --- a/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/Figuredata.java +++ b/src/main/java/com/eu/habbo/habbohotel/users/clothingvalidation/Figuredata.java @@ -9,7 +9,11 @@ import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; import java.io.IOException; +import java.io.StringWriter; import java.util.Map; import java.util.TreeMap; @@ -38,12 +42,11 @@ public class Figuredata { Element rootElement = document.getDocumentElement(); - if(!rootElement.getTagName().equalsIgnoreCase("figuredata")) { - throw new Exception("The passed file is not in figuredata format. Received " + document.toString()); - } - - if(document.getElementsByTagName("colors") == null || document.getElementsByTagName("sets") == null) { - throw new Exception("The passed file is not in figuredata format. Received " + document.toString()); + if(!rootElement.getTagName().equalsIgnoreCase("figuredata") || document.getElementsByTagName("colors") == null || document.getElementsByTagName("sets") == null) { + StringWriter writer = new StringWriter(); + TransformerFactory.newInstance().newTransformer().transform(new DOMSource(document), new StreamResult(writer)); + String documentString = writer.getBuffer().toString(); + throw new Exception("The passed file is not in figuredata format. Received " + documentString.substring(0, Math.min(documentString.length(), 200))); } NodeList palettesList = document.getElementsByTagName("colors").item(0).getChildNodes(); From bcc807f7b5d855cb01755c8fb3704d24396005f2 Mon Sep 17 00:00:00 2001 From: Beny Date: Tue, 6 Oct 2020 01:08:51 +0200 Subject: [PATCH 5/5] Fixed wrong Valid until date calculations --- .../eu/habbo/habbohotel/catalog/ClubOffer.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/eu/habbo/habbohotel/catalog/ClubOffer.java b/src/main/java/com/eu/habbo/habbohotel/catalog/ClubOffer.java index e615e13d..3be967be 100644 --- a/src/main/java/com/eu/habbo/habbohotel/catalog/ClubOffer.java +++ b/src/main/java/com/eu/habbo/habbohotel/catalog/ClubOffer.java @@ -7,6 +7,7 @@ import com.eu.habbo.messages.ServerMessage; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Calendar; +import java.util.TimeZone; public class ClubOffer implements ISerialize { @@ -91,17 +92,17 @@ public class ClubOffer implements ISerialize { message.appendInt(this.pointsType); message.appendBoolean(this.vip); - long seconds = this.days * 86400L; + long seconds = this.days * 86400; long secondsTotal = seconds; - int totalYears = (int) Math.floor((int) seconds / 86400 * 31 * 12); - seconds -= totalYears * 86400 * 31 * 12; + int totalYears = (int) Math.floor((int) seconds / (86400.0 * 31 * 12)); + seconds -= totalYears * (86400 * 31 * 12); - int totalMonths = (int) Math.floor((int) seconds / 86400 * 31); - seconds -= totalMonths * 86400 * 31; + int totalMonths = (int) Math.floor((int) seconds / (86400.0 * 31)); + seconds -= totalMonths * (86400 * 31); - int totalDays = (int) Math.floor((int) seconds / 86400); + int totalDays = (int) Math.floor((int) seconds / 86400.0); seconds -= totalDays * 86400; message.appendInt((int) secondsTotal / 86400 / 31); @@ -112,9 +113,10 @@ public class ClubOffer implements ISerialize { hcExpireTimestamp += secondsTotal; Calendar cal = Calendar.getInstance(); + cal.setTimeZone(TimeZone.getTimeZone("UTC")); cal.setTimeInMillis(hcExpireTimestamp * 1000L); message.appendInt(cal.get(Calendar.YEAR)); - message.appendInt(cal.get(Calendar.MONTH)); + message.appendInt(cal.get(Calendar.MONTH) + 1); message.appendInt(cal.get(Calendar.DAY_OF_MONTH)); } } \ No newline at end of file