From 64b0f499d0a9b32d1f62246616d19a64b7808563 Mon Sep 17 00:00:00 2001 From: Beny Date: Sun, 4 Oct 2020 00:25:55 +0200 Subject: [PATCH] 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; + } +}