Merge pull request #144 from duckietm/dev

Dev
This commit is contained in:
DuckieTM
2026-06-03 09:47:03 +02:00
committed by GitHub
16 changed files with 229 additions and 186 deletions
@@ -322,13 +322,6 @@ CREATE TABLE IF NOT EXISTS `custom_prefix_settings` (
PRIMARY KEY (`key_name`) PRIMARY KEY (`key_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `custom_prefix_blacklist` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`word` VARCHAR(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_custom_prefix_blacklist_word` (`word`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT IGNORE INTO `custom_prefix_settings` (`key_name`, `value`) VALUES INSERT IGNORE INTO `custom_prefix_settings` (`key_name`, `value`) VALUES
('max_length', '15'), ('max_length', '15'),
('min_rank_to_buy', '1'), ('min_rank_to_buy', '1'),
@@ -63,15 +63,6 @@ CREATE TABLE IF NOT EXISTS `custom_prefix_settings` (
PRIMARY KEY (`key_name`) PRIMARY KEY (`key_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ------------------------------------------------------------
-- 5. Blacklist table
-- ------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `custom_prefix_blacklist` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`word` VARCHAR(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_word` (`word`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ============================================================ -- ============================================================
-- Schema upgrades for existing installations -- Schema upgrades for existing installations
@@ -296,14 +287,6 @@ INSERT IGNORE INTO `custom_prefixes_catalog`
(2, 'Legend', 'Legend', '#8B5CF6', '', 'discord-neon', '', 15, 0, 1, 2), (2, 'Legend', 'Legend', '#8B5CF6', '', 'discord-neon', '', 15, 0, 1, 2),
(3, 'Staff Pick', 'Staff', '#3B82F6', '*', 'cartoon', '', 20, 0, 1, 3); (3, 'Staff Pick', 'Staff', '#3B82F6', '*', 'cartoon', '', 20, 0, 1, 3);
-- ============================================================
-- Example blacklist entries
-- ============================================================
INSERT IGNORE INTO `custom_prefix_blacklist` (`word`) VALUES
('admin'),
('staff'),
('mod'),
('owner');
-- ============================================================ -- ============================================================
-- Notes -- Notes
@@ -301,7 +301,6 @@ public class CommandHandler {
addCommand(new GivePrefixCommand()); addCommand(new GivePrefixCommand());
addCommand(new ListPrefixesCommand()); addCommand(new ListPrefixesCommand());
addCommand(new RemovePrefixCommand()); addCommand(new RemovePrefixCommand());
addCommand(new PrefixBlacklistCommand());
addCommand(new WiredCommand()); addCommand(new WiredCommand());
addCommand(new TestCommand()); addCommand(new TestCommand());
} }
@@ -1,98 +0,0 @@
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class PrefixBlacklistCommand extends Command {
private static final Logger LOGGER = LoggerFactory.getLogger(PrefixBlacklistCommand.class);
public PrefixBlacklistCommand() {
super("cmd_prefix_blacklist", Emulator.getTexts().getValue("commands.keys.cmd_prefix_blacklist").split(";"));
}
@Override
public boolean handle(GameClient gameClient, String[] params) throws Exception {
if (params.length < 2) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_prefix_blacklist.usage"), RoomChatMessageBubbles.ALERT);
return true;
}
String action = params[1].toLowerCase();
if (action.equals("list")) {
StringBuilder sb = new StringBuilder();
sb.append(Emulator.getTexts().getValue("commands.succes.cmd_prefix_blacklist.header")).append("\r");
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
PreparedStatement statement = connection.prepareStatement("SELECT word FROM custom_prefix_blacklist ORDER BY word")) {
try (ResultSet set = statement.executeQuery()) {
int count = 0;
while (set.next()) {
sb.append("- ").append(set.getString("word")).append("\r");
count++;
}
if (count == 0) {
sb.append(Emulator.getTexts().getValue("commands.succes.cmd_prefix_blacklist.empty"));
}
}
} catch (SQLException e) {
LOGGER.error("Error listing prefix blacklist", e);
}
gameClient.getHabbo().whisper(sb.toString(), RoomChatMessageBubbles.ALERT);
return true;
}
if (params.length < 3) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_prefix_blacklist.usage"), RoomChatMessageBubbles.ALERT);
return true;
}
String word = params[2].toLowerCase().trim();
if (word.isEmpty()) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_prefix_blacklist.empty_word"), RoomChatMessageBubbles.ALERT);
return true;
}
if (action.equals("add")) {
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
PreparedStatement statement = connection.prepareStatement("INSERT INTO custom_prefix_blacklist (word) VALUES (?)")) {
statement.setString(1, word);
statement.execute();
} catch (SQLException e) {
LOGGER.error("Error adding prefix blacklist word", e);
}
gameClient.getHabbo().whisper(
Emulator.getTexts().getValue("commands.succes.cmd_prefix_blacklist.added").replace("%word%", word),
RoomChatMessageBubbles.ALERT
);
} else if (action.equals("remove")) {
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
PreparedStatement statement = connection.prepareStatement("DELETE FROM custom_prefix_blacklist WHERE word = ?")) {
statement.setString(1, word);
statement.execute();
} catch (SQLException e) {
LOGGER.error("Error removing prefix blacklist word", e);
}
gameClient.getHabbo().whisper(
Emulator.getTexts().getValue("commands.succes.cmd_prefix_blacklist.removed").replace("%word%", word),
RoomChatMessageBubbles.ALERT
);
} else {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_prefix_blacklist.usage"), RoomChatMessageBubbles.ALERT);
}
return true;
}
}
@@ -252,6 +252,25 @@ public class Guild implements Runnable {
return this.readForum; return this.readForum;
} }
public boolean canHabboReadForum(int habboId, GuildMember member, boolean staff) {
if (staff || this.getOwnerId() == habboId) {
return true;
}
switch (this.readForum) {
case EVERYONE:
return true;
case MEMBERS:
return member != null && member.getRank().type <= GuildRank.MEMBER.type;
case ADMINS:
return member != null && member.getRank().type < GuildRank.MEMBER.type;
case OWNER:
return false;
default:
return true;
}
}
public void setReadForum(SettingsState readForum) { public void setReadForum(SettingsState readForum) {
this.readForum = readForum; this.readForum = readForum;
} }
@@ -29,6 +29,11 @@ public class GuildDeclineMembershipEvent extends MessageHandler {
if (guild != null) { if (guild != null) {
GuildMember member = Emulator.getGameEnvironment().getGuildManager().getGuildMember(guild, this.client.getHabbo()); GuildMember member = Emulator.getGameEnvironment().getGuildManager().getGuildMember(guild, this.client.getHabbo());
if (userId == this.client.getHabbo().getHabboInfo().getId() || guild.getOwnerId() == this.client.getHabbo().getHabboInfo().getId() || (member != null && (member.getRank().equals(GuildRank.ADMIN) || member.getRank().equals(GuildRank.OWNER))) || this.client.getHabbo().hasPermission(Permission.ACC_GUILD_ADMIN)) { if (userId == this.client.getHabbo().getHabboInfo().getId() || guild.getOwnerId() == this.client.getHabbo().getHabboInfo().getId() || (member != null && (member.getRank().equals(GuildRank.ADMIN) || member.getRank().equals(GuildRank.OWNER))) || this.client.getHabbo().hasPermission(Permission.ACC_GUILD_ADMIN)) {
GuildMember target = Emulator.getGameEnvironment().getGuildManager().getGuildMember(guildId, userId);
if (target == null || target.getRank().type != GuildRank.REQUESTED.type) {
return;
}
guild.decreaseRequestCount(); guild.decreaseRequestCount();
Emulator.getGameEnvironment().getGuildManager().removeMember(guild, userId); Emulator.getGameEnvironment().getGuildManager().removeMember(guild, userId);
this.client.sendResponse(new GuildMembersComposer(guild, Emulator.getGameEnvironment().getGuildManager().getGuildMembers(guild, 0, 0, ""), this.client.getHabbo(), 0, 0, "", true, Emulator.getGameEnvironment().getGuildManager().getGuildMembersCount(guild, 0, 0, ""))); this.client.sendResponse(new GuildMembersComposer(guild, Emulator.getGameEnvironment().getGuildManager().getGuildMembers(guild, 0, 0, ""), this.client.getHabbo(), 0, 0, "", true, Emulator.getGameEnvironment().getGuildManager().getGuildMembersCount(guild, 0, 0, "")));
@@ -2,7 +2,11 @@ package com.eu.habbo.messages.incoming.guilds.forums;
import com.eu.habbo.Emulator; import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.guilds.Guild; import com.eu.habbo.habbohotel.guilds.Guild;
import com.eu.habbo.habbohotel.guilds.GuildMember;
import com.eu.habbo.habbohotel.permissions.Permission;
import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertComposer;
import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertKeys;
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumDataComposer; import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumDataComposer;
public class GuildForumDataEvent extends MessageHandler { public class GuildForumDataEvent extends MessageHandler {
@@ -20,10 +24,18 @@ public class GuildForumDataEvent extends MessageHandler {
if (guild == null) return; if (guild == null) return;
if (!guild.hasForum()) return; if (!guild.hasForum()) return;
GuildMember member = Emulator.getGameEnvironment().getGuildManager().getGuildMember(guildId, this.client.getHabbo().getHabboInfo().getId());
boolean staff = this.client.getHabbo().hasPermission(Permission.ACC_MODTOOL_TICKET_Q);
if (!guild.canHabboReadForum(this.client.getHabbo().getHabboInfo().getId(), member, staff)) {
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FORUMS_ACCESS_DENIED.key).compose());
return;
}
this.client.sendResponse(new GuildForumDataComposer(guild, this.client.getHabbo())); this.client.sendResponse(new GuildForumDataComposer(guild, this.client.getHabbo()));
if (!Emulator.getGameEnvironment().getGuildManager().hasViewedForum(this.client.getHabbo().getHabboInfo().getId(), guildId)) { if (!Emulator.getGameEnvironment().getGuildManager().hasViewedForum(this.client.getHabbo().getHabboInfo().getId(), guildId)) {
Emulator.getGameEnvironment().getGuildManager().addView(this.client.getHabbo().getHabboInfo().getId(), guildId); Emulator.getGameEnvironment().getGuildManager().addView(this.client.getHabbo().getHabboInfo().getId(), guildId);
} }
} }
} }
@@ -2,7 +2,11 @@ package com.eu.habbo.messages.incoming.guilds.forums;
import com.eu.habbo.Emulator; import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.guilds.Guild; import com.eu.habbo.habbohotel.guilds.Guild;
import com.eu.habbo.habbohotel.guilds.GuildMember;
import com.eu.habbo.habbohotel.permissions.Permission;
import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertComposer;
import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertKeys;
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumDataComposer; import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumDataComposer;
import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumThreadsComposer; import com.eu.habbo.messages.outgoing.guilds.forums.GuildForumThreadsComposer;
import com.eu.habbo.messages.outgoing.handshake.ConnectionErrorComposer; import com.eu.habbo.messages.outgoing.handshake.ConnectionErrorComposer;
@@ -24,8 +28,15 @@ public class GuildForumThreadsEvent extends MessageHandler {
this.client.sendResponse(new ConnectionErrorComposer(404)); this.client.sendResponse(new ConnectionErrorComposer(404));
return; return;
} }
GuildMember member = Emulator.getGameEnvironment().getGuildManager().getGuildMember(guildId, this.client.getHabbo().getHabboInfo().getId());
boolean staff = this.client.getHabbo().hasPermission(Permission.ACC_MODTOOL_TICKET_Q);
if (!guild.canHabboReadForum(this.client.getHabbo().getHabboInfo().getId(), member, staff)) {
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FORUMS_ACCESS_DENIED.key).compose());
return;
}
this.client.sendResponse(new GuildForumDataComposer(guild, this.client.getHabbo())); this.client.sendResponse(new GuildForumDataComposer(guild, this.client.getHabbo()));
this.client.sendResponse(new GuildForumThreadsComposer(guild, index)); this.client.sendResponse(new GuildForumThreadsComposer(guild, index));
} }
} }
@@ -38,7 +38,6 @@ public class GuildForumThreadsMessagesEvent extends MessageHandler {
return; return;
} }
// Verify thread belongs to the requested guild
if (thread.getGuildId() != guildId) { if (thread.getGuildId() != guildId) {
this.client.sendResponse(new ConnectionErrorComposer(403)); this.client.sendResponse(new ConnectionErrorComposer(403));
return; return;
@@ -47,6 +46,11 @@ public class GuildForumThreadsMessagesEvent extends MessageHandler {
GuildMember member = Emulator.getGameEnvironment().getGuildManager().getGuildMember(guildId, this.client.getHabbo().getHabboInfo().getId()); GuildMember member = Emulator.getGameEnvironment().getGuildManager().getGuildMember(guildId, this.client.getHabbo().getHabboInfo().getId());
boolean isGuildAdministrator = (guild.getOwnerId() == this.client.getHabbo().getHabboInfo().getId() || (member != null && member.getRank().equals(GuildRank.ADMIN))); boolean isGuildAdministrator = (guild.getOwnerId() == this.client.getHabbo().getHabboInfo().getId() || (member != null && member.getRank().equals(GuildRank.ADMIN)));
if (!guild.canHabboReadForum(this.client.getHabbo().getHabboInfo().getId(), member, hasStaffPermissions)) {
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FORUMS_ACCESS_DENIED.key).compose());
return;
}
if (thread.getState() != ForumThreadState.HIDDEN_BY_GUILD_ADMIN || hasStaffPermissions || isGuildAdministrator) { if (thread.getState() != ForumThreadState.HIDDEN_BY_GUILD_ADMIN || hasStaffPermissions || isGuildAdministrator) {
this.client.sendResponse(new GuildForumCommentsComposer(guildId, threadId, index, thread.getComments(limit, index))); this.client.sendResponse(new GuildForumCommentsComposer(guildId, threadId, index, thread.getComments(limit, index)));
this.client.sendResponse(new GuildForumDataComposer(guild, this.client.getHabbo())); this.client.sendResponse(new GuildForumDataComposer(guild, this.client.getHabbo()));
@@ -6,6 +6,11 @@ import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.inventory.prefixes.UserPrefixesComposer; import com.eu.habbo.messages.outgoing.inventory.prefixes.UserPrefixesComposer;
public class DeletePrefixEvent extends MessageHandler { public class DeletePrefixEvent extends MessageHandler {
@Override
public int getRatelimit() {
return 500;
}
@Override @Override
public void handle() throws Exception { public void handle() throws Exception {
int prefixId = this.packet.readInt(); int prefixId = this.packet.readInt();
@@ -1,15 +1,20 @@
package com.eu.habbo.messages.incoming.inventory.prefixes; package com.eu.habbo.messages.incoming.inventory.prefixes;
import com.eu.habbo.Emulator; import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.modtool.WordFilterWord;
import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.UserPrefix; import com.eu.habbo.habbohotel.users.UserPrefix;
import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertKeys;
import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertComposer; import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertComposer;
import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertKeys;
import com.eu.habbo.messages.outgoing.inventory.nickicons.UserNickIconsComposer; import com.eu.habbo.messages.outgoing.inventory.nickicons.UserNickIconsComposer;
import com.eu.habbo.messages.outgoing.inventory.prefixes.ActivePrefixUpdatedComposer;
import com.eu.habbo.messages.outgoing.inventory.prefixes.CustomPrefixPurchaseFailedComposer;
import com.eu.habbo.messages.outgoing.inventory.prefixes.PrefixReceivedComposer; import com.eu.habbo.messages.outgoing.inventory.prefixes.PrefixReceivedComposer;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDataComposer;
import com.eu.habbo.messages.outgoing.users.UserCreditsComposer; import com.eu.habbo.messages.outgoing.users.UserCreditsComposer;
import com.eu.habbo.messages.outgoing.users.UserCurrencyComposer; import com.eu.habbo.messages.outgoing.users.UserCurrencyComposer;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -17,10 +22,18 @@ import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
public class PurchasePrefixEvent extends MessageHandler { public class PurchasePrefixEvent extends MessageHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(PurchasePrefixEvent.class); private static final Logger LOGGER = LoggerFactory.getLogger(PurchasePrefixEvent.class);
private static final String[] ALLOWED_FONTS = { "", "pixel", "cherry", "vampiro" }; private static final String[] ALLOWED_FONTS = { "", "pixel", "cherry", "vampiro" };
private static final String[] ALLOWED_EFFECTS = {
"", "glow", "shadow", "italic", "outline", "underline", "pulse", "bounce", "wave", "shake",
"discord-neon", "cartoon", "toon", "pop", "bold-glow", "rainbow", "frost", "gold", "glitch",
"fire", "matrix", "sparkle"
};
private static final int MAX_ICON_LENGTH = 16;
@Override @Override
public int getRatelimit() { public int getRatelimit() {
@@ -39,81 +52,101 @@ public class PurchasePrefixEvent extends MessageHandler {
if (habbo == null) return; if (habbo == null) return;
// Load settings Map<String, Integer> settings = loadSettings();
int maxLength = getSettingInt("max_length", 15); int maxLength = setting(settings, "max_length", 15);
int minRank = getSettingInt("min_rank_to_buy", 1); int minRank = setting(settings, "min_rank_to_buy", 1);
int priceCredits = getSettingInt("price_credits", 5); int priceCredits = setting(settings, "price_credits", 5);
int pricePoints = getSettingInt("price_points", 0); int pricePoints = setting(settings, "price_points", 0);
int pointsType = getSettingInt("points_type", 0); int pointsType = setting(settings, "points_type", 0);
int fontPriceCredits = getSettingInt("font_price_credits", 10); int fontPriceCredits = setting(settings, "font_price_credits", 10);
int fontPricePoints = getSettingInt("font_price_points", 0); int fontPricePoints = setting(settings, "font_price_points", 0);
int fontPointsType = getSettingInt("font_points_type", pointsType); int fontPointsType = setting(settings, "font_points_type", pointsType);
int maxPrefixes = setting(settings, "max_prefixes", 60);
// Validate text if (maxPrefixes > 0 && habbo.getInventory().getPrefixesComponent().getPrefixes().size() >= maxPrefixes) {
text = text.trim(); this.fail(habbo, "You already own the maximum number of prefixes (" + maxPrefixes + ").");
return;
if (text.isEmpty() || text.length() > maxLength) { }
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FURNITURE_PLACEMENT_ERROR.key, "Prefix text is invalid or too long (max " + maxLength + " characters)."));
text = text.trim();
if (text.isEmpty() || text.length() > maxLength) {
this.fail(habbo, "Prefix text is invalid or too long (max " + maxLength + " characters).");
return;
}
if (containsControlChars(text)) {
this.fail(habbo, "Prefix text contains invalid characters.");
return;
}
if (containsFilteredWord(text)) {
this.fail(habbo, "This prefix contains a blocked word.");
return; return;
} }
// Validate color (single hex or comma-separated multi hex for per-letter colors)
String[] colorParts = color.split(","); String[] colorParts = color.split(",");
if (colorParts.length > text.length()) {
this.fail(habbo, "Invalid color format.");
return;
}
for (String part : colorParts) { for (String part : colorParts) {
if (!part.matches("^#[0-9A-Fa-f]{6}$")) { if (!part.matches("^#[0-9A-Fa-f]{6}$")) {
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FURNITURE_PLACEMENT_ERROR.key, "Invalid color format.")); this.fail(habbo, "Invalid color format.");
return; return;
} }
} }
// Check rank
if (habbo.getHabboInfo().getRank().getId() < minRank) { if (habbo.getHabboInfo().getRank().getId() < minRank) {
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FURNITURE_PLACEMENT_ERROR.key, "Your rank is too low to purchase prefixes.")); this.fail(habbo, "Your rank is too low to purchase prefixes.");
return;
}
// Check blacklist
if (isBlacklisted(text)) {
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FURNITURE_PLACEMENT_ERROR.key, "This prefix contains a blocked word."));
return; return;
} }
if (icon == null) icon = ""; if (icon == null) icon = "";
icon = icon.trim(); icon = icon.trim();
if (!isValidIcon(icon)) {
this.fail(habbo, "Invalid prefix icon.");
return;
}
if (effect == null) effect = ""; if (effect == null) effect = "";
effect = effect.trim(); effect = effect.trim().toLowerCase();
if (!isAllowedEffect(effect)) {
this.fail(habbo, "Invalid prefix effect.");
return;
}
if (font == null) font = ""; if (font == null) font = "";
font = font.trim().toLowerCase(); font = font.trim().toLowerCase();
if (!isAllowedFont(font)) { if (!isAllowedFont(font)) {
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FURNITURE_PLACEMENT_ERROR.key, "Invalid font format.")); this.fail(habbo, "Invalid font format.");
return; return;
} }
int totalPriceCredits = priceCredits + (!font.isEmpty() ? fontPriceCredits : 0); int totalPriceCredits = priceCredits + (!font.isEmpty() ? fontPriceCredits : 0);
// Check credits
if (totalPriceCredits > 0 && habbo.getHabboInfo().getCredits() < totalPriceCredits) { if (totalPriceCredits > 0 && habbo.getHabboInfo().getCredits() < totalPriceCredits) {
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FURNITURE_PLACEMENT_ERROR.key, "Not enough credits.")); this.fail(habbo, "Not enough credits.");
return; return;
} }
int totalPricePointsSameType = pricePoints + ((fontPricePoints > 0 && fontPointsType == pointsType && !font.isEmpty()) ? fontPricePoints : 0); int totalPricePointsSameType = pricePoints + ((fontPricePoints > 0 && fontPointsType == pointsType && !font.isEmpty()) ? fontPricePoints : 0);
// Check points
if (totalPricePointsSameType > 0 && habbo.getHabboInfo().getCurrencyAmount(pointsType) < totalPricePointsSameType) { if (totalPricePointsSameType > 0 && habbo.getHabboInfo().getCurrencyAmount(pointsType) < totalPricePointsSameType) {
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FURNITURE_PLACEMENT_ERROR.key, "Not enough points.")); this.fail(habbo, "Not enough points.");
return; return;
} }
if (!font.isEmpty() && fontPricePoints > 0 && fontPointsType != pointsType && habbo.getHabboInfo().getCurrencyAmount(fontPointsType) < fontPricePoints) { if (!font.isEmpty() && fontPricePoints > 0 && fontPointsType != pointsType && habbo.getHabboInfo().getCurrencyAmount(fontPointsType) < fontPricePoints) {
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FURNITURE_PLACEMENT_ERROR.key, "Not enough points.")); this.fail(habbo, "Not enough points.");
return; return;
} }
// Deduct currency
if (totalPriceCredits > 0) { if (totalPriceCredits > 0) {
habbo.getHabboInfo().addCredits(-totalPriceCredits); habbo.getHabboInfo().addCredits(-totalPriceCredits);
this.client.sendResponse(new UserCreditsComposer(habbo)); this.client.sendResponse(new UserCreditsComposer(habbo));
@@ -129,47 +162,57 @@ public class PurchasePrefixEvent extends MessageHandler {
this.client.sendResponse(new UserCurrencyComposer(habbo)); this.client.sendResponse(new UserCurrencyComposer(habbo));
} }
// Create prefix
int storedPoints = totalPricePointsSameType; int storedPoints = totalPricePointsSameType;
int storedPointsType = (storedPoints > 0) ? pointsType : ((!font.isEmpty() && fontPricePoints > 0) ? fontPointsType : pointsType); int storedPointsType = (storedPoints > 0) ? pointsType : ((!font.isEmpty() && fontPricePoints > 0) ? fontPointsType : pointsType);
UserPrefix prefix = new UserPrefix(habbo.getHabboInfo().getId(), text, color, icon, effect, font, 0, text, storedPoints, storedPointsType, true); UserPrefix prefix = new UserPrefix(habbo.getHabboInfo().getId(), text, color, icon, effect, font, 0, text, storedPoints, storedPointsType, true);
prefix.run(); // Insert into DB synchronously to get the ID prefix.run(); // Insert into DB synchronously to get the ID
habbo.getInventory().getPrefixesComponent().addPrefix(prefix); habbo.getInventory().getPrefixesComponent().addPrefix(prefix);
habbo.getInventory().getPrefixesComponent().setActive(prefix.getId());
this.client.sendResponse(new PrefixReceivedComposer(prefix)); this.client.sendResponse(new PrefixReceivedComposer(prefix));
this.client.sendResponse(new ActivePrefixUpdatedComposer(prefix));
this.client.sendResponse(new UserNickIconsComposer(habbo)); this.client.sendResponse(new UserNickIconsComposer(habbo));
}
private int getSettingInt(String key, int defaultValue) { if (habbo.getHabboInfo().getCurrentRoom() != null) {
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); habbo.getHabboInfo().getCurrentRoom().sendComposer(new RoomUserDataComposer(habbo).compose());
PreparedStatement statement = connection.prepareStatement("SELECT `value` FROM custom_prefix_settings WHERE key_name = ?")) {
statement.setString(1, key);
try (ResultSet set = statement.executeQuery()) {
if (set.next()) {
return Integer.parseInt(set.getString("value"));
}
}
} catch (SQLException | NumberFormatException e) {
LOGGER.error("Error reading prefix setting: " + key, e);
} }
return defaultValue;
} }
private boolean isBlacklisted(String text) { private void fail(Habbo habbo, String message) {
String lowerText = text.toLowerCase(); this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FURNITURE_PLACEMENT_ERROR.key, message));
this.client.sendResponse(new CustomPrefixPurchaseFailedComposer(message));
}
private Map<String, Integer> loadSettings() {
Map<String, Integer> settings = new HashMap<>();
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
PreparedStatement statement = connection.prepareStatement("SELECT word FROM custom_prefix_blacklist")) { PreparedStatement statement = connection.prepareStatement("SELECT key_name, `value` FROM custom_prefix_settings");
try (ResultSet set = statement.executeQuery()) { ResultSet set = statement.executeQuery()) {
while (set.next()) { while (set.next()) {
if (lowerText.contains(set.getString("word").toLowerCase())) { try {
return true; settings.put(set.getString("key_name"), Integer.parseInt(set.getString("value")));
} } catch (NumberFormatException ignored) {}
}
} }
} catch (SQLException e) { } catch (SQLException e) {
LOGGER.error("Error checking prefix blacklist", e); LOGGER.error("Error reading prefix settings", e);
} }
return settings;
}
private int setting(Map<String, Integer> settings, String key, int defaultValue) {
Integer value = settings.get(key);
return value != null ? value : defaultValue;
}
private boolean containsFilteredWord(String text) {
if (text == null || text.isEmpty()) return false;
for (WordFilterWord word : Emulator.getGameEnvironment().getWordFilter().getWords()) {
if (word.key != null && !word.key.isEmpty() && StringUtils.containsIgnoreCase(text, word.key)) {
return true;
}
}
return false; return false;
} }
@@ -182,4 +225,35 @@ public class PurchasePrefixEvent extends MessageHandler {
return false; return false;
} }
private boolean isAllowedEffect(String effect) {
for (String allowedEffect : ALLOWED_EFFECTS) {
if (allowedEffect.equals(effect)) {
return true;
}
}
return false;
}
private boolean isValidIcon(String icon) {
if (icon.isEmpty()) return true;
if (icon.length() > MAX_ICON_LENGTH) return false;
for (int i = 0; i < icon.length(); i++) {
char c = icon.charAt(i);
if (c < 0x20 || c == 0x7F || c == '<' || c == '>') return false;
}
return true;
}
private boolean containsControlChars(String text) {
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c < 0x20 || c == 0x7F) return true;
}
return false;
}
} }
@@ -4,6 +4,11 @@ import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.inventory.prefixes.UserPrefixesComposer; import com.eu.habbo.messages.outgoing.inventory.prefixes.UserPrefixesComposer;
public class RequestUserPrefixesEvent extends MessageHandler { public class RequestUserPrefixesEvent extends MessageHandler {
@Override
public int getRatelimit() {
return 500;
}
@Override @Override
public void handle() throws Exception { public void handle() throws Exception {
this.client.sendResponse(new UserPrefixesComposer(this.client.getHabbo())); this.client.sendResponse(new UserPrefixesComposer(this.client.getHabbo()));
@@ -2,11 +2,16 @@ package com.eu.habbo.messages.incoming.inventory.prefixes;
import com.eu.habbo.habbohotel.users.UserPrefix; import com.eu.habbo.habbohotel.users.UserPrefix;
import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.inventory.prefixes.ActivePrefixUpdatedComposer;
import com.eu.habbo.messages.outgoing.inventory.nickicons.UserNickIconsComposer; import com.eu.habbo.messages.outgoing.inventory.nickicons.UserNickIconsComposer;
import com.eu.habbo.messages.outgoing.inventory.prefixes.ActivePrefixUpdatedComposer;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDataComposer; import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDataComposer;
public class SetActivePrefixEvent extends MessageHandler { public class SetActivePrefixEvent extends MessageHandler {
@Override
public int getRatelimit() {
return 1000;
}
@Override @Override
public void handle() throws Exception { public void handle() throws Exception {
int prefixId = this.packet.readInt(); int prefixId = this.packet.readInt();
@@ -7,6 +7,11 @@ import com.eu.habbo.messages.outgoing.inventory.nickicons.UserNickIconsComposer;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDataComposer; import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDataComposer;
public class SetDisplayOrderEvent extends MessageHandler { public class SetDisplayOrderEvent extends MessageHandler {
@Override
public int getRatelimit() {
return 1000;
}
@Override @Override
public void handle() throws Exception { public void handle() throws Exception {
Habbo habbo = this.client.getHabbo(); Habbo habbo = this.client.getHabbo();
@@ -23,4 +28,4 @@ public class SetDisplayOrderEvent extends MessageHandler {
habbo.getHabboInfo().getCurrentRoom().sendComposer(new RoomUserDataComposer(habbo).compose()); habbo.getHabboInfo().getCurrentRoom().sendComposer(new RoomUserDataComposer(habbo).compose());
} }
} }
} }
@@ -579,6 +579,7 @@ public class Outgoing {
public static final int PrefixReceivedComposer = 7002; public static final int PrefixReceivedComposer = 7002;
public static final int ActivePrefixUpdatedComposer = 7003; public static final int ActivePrefixUpdatedComposer = 7003;
public static final int UserNickIconsComposer = 7004; public static final int UserNickIconsComposer = 7004;
public static final int CustomPrefixPurchaseFailedComposer = 7005;
public static final int AvailableCommandsComposer = 4050; public static final int AvailableCommandsComposer = 4050;
// YouTube Room Broadcast // YouTube Room Broadcast
@@ -0,0 +1,20 @@
package com.eu.habbo.messages.outgoing.inventory.prefixes;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.outgoing.MessageComposer;
import com.eu.habbo.messages.outgoing.Outgoing;
public class CustomPrefixPurchaseFailedComposer extends MessageComposer {
private final String message;
public CustomPrefixPurchaseFailedComposer(String message) {
this.message = message != null ? message : "";
}
@Override
protected ServerMessage composeInternal() {
this.response.init(Outgoing.CustomPrefixPurchaseFailedComposer);
this.response.appendString(this.message);
return this.response;
}
}