Merge branch 'dev' into fix/wired-match-payloads

This commit is contained in:
DuckieTM
2026-06-18 12:45:43 +02:00
committed by GitHub
212 changed files with 4257 additions and 537 deletions
@@ -166,6 +166,7 @@ public final class Emulator {
Emulator.config.register("rcon.rate_limit.timeout_ms", "0");
Emulator.config.register("rcon.execute_command.denied_permissions", "cmd_shutdown;cmd_give_rank");
Emulator.config.register("rcon.execute_command.allowed_permissions", "");
Emulator.config.register("rcon.max_payload_bytes", "65536");
registerEarningsSettings();
String hotelTimezoneId = Emulator.getConfig().getValue("hotel.timezone", java.time.ZoneId.systemDefault().getId());
System.out.println(startupCard(hotelTimezoneId));
@@ -174,6 +174,14 @@ public class CatalogItem implements ISerialize, Runnable, Comparable<CatalogItem
return this.offerId;
}
public int getSearchOfferId() {
if (this.offerId > 0) {
return this.offerId;
}
return haveOffer(this) ? this.id : -1;
}
public boolean isLimited() {
return this.limitedStack > 0;
}
@@ -494,10 +494,11 @@ public class CatalogManager {
item = new CatalogItem(set);
page.addItem(item);
if (item.getOfferId() != -1) {
page.addOfferId(item.getOfferId());
int searchOfferId = item.getSearchOfferId();
if (searchOfferId != -1) {
page.addOfferId(searchOfferId);
this.offerDefs.put(item.getOfferId(), item.getId());
this.offerDefs.put(searchOfferId, item.getId());
}
} else
item.update(set);
@@ -58,6 +58,10 @@ public class MarketPlace {
public static void takeBackItem(Habbo habbo, int offerId) {
MarketPlaceOffer offer = habbo.getInventory().getOffer(offerId);
if (offer == null) {
return;
}
if (!Emulator.getPluginManager().fireEvent(new MarketPlaceItemCancelledEvent(offer)).isCancelled()) {
takeBackItem(habbo, offer);
}
@@ -32,6 +32,11 @@ public class AlertCommand extends Command {
Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(targetUsername);
if (habbo != null) {
if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), habbo)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true;
}
habbo.alert(message + "\r\n -" + gameClient.getHabbo().getHabboInfo().getUsername());
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.succes.cmd_alert.message_send").replace("%user%", targetUsername), RoomChatMessageBubbles.ALERT);
} else {
@@ -60,7 +60,7 @@ public class BanCommand extends Command {
return true;
}
if (target.getRank().getId() >= gameClient.getHabbo().getHabboInfo().getRank().getId()) {
if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), target)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true;
}
@@ -0,0 +1,46 @@
package com.eu.habbo.habbohotel.commands;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.permissions.Rank;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboInfo;
final class CommandTargetGuard {
private CommandTargetGuard() {
}
static boolean canTarget(Habbo moderator, Habbo target) {
return target != null && canTarget(moderator, target.getHabboInfo());
}
static boolean canTarget(Habbo moderator, HabboInfo target) {
if (moderator == null || target == null || moderator.getHabboInfo().getId() == target.getId()) {
return false;
}
int moderatorRankId = moderator.getHabboInfo().getRank().getId();
int targetRankId = target.getRank().getId();
return targetRankId < moderatorRankId || isCoreRank(moderatorRankId) && targetRankId <= moderatorRankId;
}
static boolean canAssignRank(Habbo moderator, Rank rank) {
if (moderator == null || rank == null) {
return false;
}
int moderatorRankId = moderator.getHabboInfo().getRank().getId();
int targetRankId = rank.getId();
return targetRankId < moderatorRankId || isCoreRank(moderatorRankId) && targetRankId <= moderatorRankId;
}
private static boolean isCoreRank(int rankId) {
int highestRankId = Emulator.getGameEnvironment().getPermissionsManager().getAllRanks().stream()
.mapToInt(Rank::getId)
.max()
.orElse(0);
return highestRankId > 0 && rankId >= highestRankId;
}
}
@@ -29,7 +29,7 @@ public class DisconnectCommand extends Command {
return true;
}
if (target.getHabboInfo().getRank().getId() > gameClient.getHabbo().getHabboInfo().getRank().getId()) {
if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), target)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_disconnect.higher_rank"), RoomChatMessageBubbles.ALERT);
return true;
}
@@ -47,6 +47,11 @@ public class GivePrefixCommand extends Command {
return true;
}
if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), target)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true;
}
UserPrefix prefix = new UserPrefix(target.getHabboInfo().getId(), text, color, icon, effect);
prefix.run();
target.getInventory().getPrefixesComponent().addPrefix(prefix);
@@ -36,7 +36,7 @@ public class GiveRankCommand extends Command {
}
if (rank != null) {
if (rank.getId() > gameClient.getHabbo().getHabboInfo().getRank().getId()) {
if (!CommandTargetGuard.canAssignRank(gameClient.getHabbo(), rank)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_give_rank.higher").replace("%username%", params[1]).replace("%id%", rank.getName()), RoomChatMessageBubbles.ALERT);
return true;
}
@@ -44,7 +44,7 @@ public class GiveRankCommand extends Command {
HabboInfo habbo = HabboManager.getOfflineHabboInfo(params[1]);
if (habbo != null) {
if (habbo.getRank().getId() > gameClient.getHabbo().getHabboInfo().getRank().getId()) {
if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), habbo)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_give_rank.higher.other").replace("%username%", params[1]).replace("%id%", rank.getName()), RoomChatMessageBubbles.ALERT);
return true;
}
@@ -63,4 +63,4 @@ public class GiveRankCommand extends Command {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.errors.cmd_give_rank.not_found").replace("%id%", params[2]).replace("%username%", params[1]), RoomChatMessageBubbles.ALERT);
return true;
}
}
}
@@ -47,7 +47,7 @@ public class IPBanCommand extends Command {
return true;
}
if (habbo.getRank().getId() >= gameClient.getHabbo().getHabboInfo().getRank().getId()) {
if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), habbo)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true;
}
@@ -43,7 +43,7 @@ public class MachineBanCommand extends Command {
return true;
}
if (habbo.getRank().getId() >= gameClient.getHabbo().getHabboInfo().getRank().getId()) {
if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), habbo)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true;
}
@@ -29,6 +29,11 @@ public class MuteCommand extends Command {
return true;
}
if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), habbo)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true;
}
int duration = Integer.MAX_VALUE;
if (params.length == 3) {
@@ -31,6 +31,11 @@ public class RemovePrefixCommand extends Command {
return true;
}
if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), target)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true;
}
if (prefixIdStr.equalsIgnoreCase("all")) {
List<UserPrefix> prefixes = target.getInventory().getPrefixesComponent().getPrefixes();
for (UserPrefix prefix : prefixes) {
@@ -41,7 +41,7 @@ public class SuperbanCommand extends Command {
return true;
}
if (habbo.getRank().getId() >= gameClient.getHabbo().getHabboInfo().getRank().getId()) {
if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), habbo)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true;
}
@@ -56,4 +56,4 @@ public class SuperbanCommand extends Command {
return true;
}
}
}
@@ -23,6 +23,11 @@ public class UnmuteCommand extends Command {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_unmute.not_found").replace("%user%", params[1]), RoomChatMessageBubbles.ALERT);
return true;
} else {
if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), habbo)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true;
}
if (!habbo.getHabboStats().allowTalk() || (habbo.getHabboInfo().getCurrentRoom() != null && habbo.getHabboInfo().getCurrentRoom().isMuted(habbo))) {
if (!habbo.getHabboStats().allowTalk()) {
habbo.unMute();
@@ -14,6 +14,7 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
public class GameClient {
@@ -24,6 +25,7 @@ public class GameClient {
private final LatencyTracker latencyTracker;
private Habbo habbo;
private final AtomicBoolean disposed = new AtomicBoolean(false);
private boolean handshakeFinished;
private String machineId = "";
private String ssoTicket = "";
@@ -153,6 +155,10 @@ public class GameClient {
}
public void dispose(boolean allowSessionResume) {
if (!this.disposed.compareAndSet(false, true)) {
return;
}
try {
this.channel.close();
@@ -441,7 +441,7 @@ public class GuildManager {
public int getGuildMembersCount(Guild guild, int page, int levelId, String query) {
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT COUNT(*) FROM guilds_members INNER JOIN users ON guilds_members.user_id = users.id WHERE guilds_members.guild_id = ? " + (rankQuery(levelId)) + " AND users.username LIKE ? ORDER BY level_id, member_since ASC")) {
statement.setInt(1, guild.getId());
statement.setString(2, "%" + query + "%");
statement.setString(2, "%" + com.eu.habbo.util.SqlLikeEscaper.escape(query) + "%");
try (ResultSet set = statement.executeQuery()) {
while (set.next()) {
@@ -45,7 +45,7 @@ public class Item implements ISerialize {
}
public static boolean isPet(Item item) {
return item.getName().toLowerCase().startsWith("a0 pet");
return item != null && item.getName() != null && item.getName().toLowerCase().startsWith("a0 pet");
}
public static boolean isBot(Item item) {
@@ -121,26 +121,19 @@ public class Item implements ISerialize {
this.customParams = set.getString("customparams");
this.clothingOnWalk = set.getString("clothing_on_walk");
if (!set.getString("vending_ids").isEmpty()) {
int[] vendingIds = ItemDataGuard.parsePositiveIntList(set.getString("vending_ids"));
if (vendingIds.length > 0) {
this.vendingItems = new TIntArrayList();
String[] vendingIds = set.getString("vending_ids").replace(";", ",").replace(".", ",").split(",");
for (String s : vendingIds) {
this.vendingItems.add(Integer.parseInt(s.replace(" ", "")));
for (int vendingId : vendingIds) {
this.vendingItems.add(vendingId);
}
} else {
this.vendingItems = new TIntArrayList();
}
//if(this.interactionType.getType() == InteractionMultiHeight.class || this.interactionType.getType().isAssignableFrom(InteractionMultiHeight.class))
{
if (set.getString("multiheight").contains(";")) {
String[] s = set.getString("multiheight").split(";");
this.multiHeights = new double[s.length];
for (int i = 0; i < s.length; i++) {
this.multiHeights[i] = Double.parseDouble(s[i]);
}
} else {
this.multiHeights = new double[0];
}
this.multiHeights = ItemDataGuard.parseHeights(set.getString("multiheight"));
}
this.rotations = 4;
@@ -254,6 +247,10 @@ public class Item implements ISerialize {
}
public int getRandomVendingItem() {
if (this.vendingItems == null || this.vendingItems.isEmpty()) {
return 0;
}
return this.vendingItems.get(Emulator.getRandom().nextInt(this.vendingItems.size()));
}
@@ -273,21 +270,23 @@ public class Item implements ISerialize {
@Override
public void serialize(ServerMessage message) {
message.appendString(this.type.code.toLowerCase());
message.appendString(this.type == null ? "" : this.type.code.toLowerCase());
if (type == FurnitureType.BADGE) {
message.appendString(this.customParams);
message.appendString(ItemDataGuard.safeString(this.customParams));
} else {
message.appendInt(this.spriteId);
if (this.getName().contains("wallpaper_single") || this.getName().contains("floor_single") || this.getName().contains("landscape_single")) {
message.appendString(this.name.split("_")[2]);
String itemName = ItemDataGuard.safeString(this.getName());
if (itemName.contains("wallpaper_single") || itemName.contains("floor_single") || itemName.contains("landscape_single")) {
String[] nameParts = itemName.split("_");
message.appendString(nameParts.length > 2 ? nameParts[2] : "");
} else if (type == FurnitureType.ROBOT) {
message.appendString(this.customParams);
} else if (name.equalsIgnoreCase("poster")) {
message.appendString(this.customParams);
} else if (name.startsWith("SONG ")) {
message.appendString(this.customParams);
message.appendString(ItemDataGuard.safeString(this.customParams));
} else if (itemName.equalsIgnoreCase("poster")) {
message.appendString(ItemDataGuard.safeString(this.customParams));
} else if (itemName.startsWith("SONG ")) {
message.appendString(ItemDataGuard.safeString(this.customParams));
} else {
message.appendString("");
}
@@ -0,0 +1,82 @@
package com.eu.habbo.habbohotel.items;
final class ItemDataGuard {
static final int MAX_EXTRA_DATA_LENGTH = 1000;
private ItemDataGuard() {
}
static String safeString(String value) {
return value == null ? "" : value;
}
static String normalizeExtraData(String value) {
String safe = safeString(value);
return safe.length() > MAX_EXTRA_DATA_LENGTH ? safe.substring(0, MAX_EXTRA_DATA_LENGTH) : safe;
}
static int parsePositiveInt(String value) {
try {
int parsed = Integer.parseInt(safeString(value).trim());
return parsed > 0 ? parsed : 0;
} catch (NumberFormatException e) {
return 0;
}
}
static int[] parsePositiveIntList(String value) {
String safe = safeString(value).replace(";", ",").replace(".", ",");
if (safe.isBlank()) {
return new int[0];
}
String[] parts = safe.split(",");
int[] parsed = new int[parts.length];
int count = 0;
for (String part : parts) {
int id = parsePositiveInt(part);
if (id > 0) {
parsed[count++] = id;
}
}
if (count == parsed.length) {
return parsed;
}
int[] compact = new int[count];
System.arraycopy(parsed, 0, compact, 0, count);
return compact;
}
static double[] parseHeights(String value) {
String safe = safeString(value);
if (safe.isBlank() || !safe.contains(";")) {
return new double[0];
}
String[] parts = safe.split(";");
double[] parsed = new double[parts.length];
int count = 0;
for (String part : parts) {
try {
double height = Double.parseDouble(part.trim());
if (Double.isFinite(height)) {
parsed[count++] = height;
}
} catch (NumberFormatException e) {
// Ignore malformed DB values and keep the remaining heights usable.
}
}
if (count == parsed.length) {
return parsed;
}
double[] compact = new double[count];
System.arraycopy(parsed, 0, compact, 0, count);
return compact;
}
}
@@ -566,6 +566,10 @@ public class ItemManager {
public int calculateCrackState(int count, int max, Item baseItem) {
if (count <= 0 || max <= 0 || baseItem == null || baseItem.getStateCount() <= 0) {
return 0;
}
return (int) Math.floor((1.0D / ((double) max / (double) count) * baseItem.getStateCount()));
}
@@ -574,7 +578,8 @@ public class ItemManager {
}
public Item getCrackableReward(int itemId) {
return this.getItem(this.crackableRewards.get(itemId).getRandomReward());
CrackableReward reward = this.crackableRewards.get(itemId);
return reward == null ? null : this.getItem(reward.getRandomReward());
}
@@ -604,6 +609,12 @@ public class ItemManager {
}
public HabboItem createItem(int habboId, Item item, int limitedStack, int limitedSells, String extraData) {
if (habboId <= 0 || item == null) {
return null;
}
extraData = ItemDataGuard.normalizeExtraData(extraData);
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO items (user_id, item_id, extra_data, limited_data) VALUES (?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS)) {
statement.setInt(1, habboId);
statement.setInt(2, item.getId());
@@ -673,6 +684,12 @@ public class ItemManager {
}
public HabboItem handleRecycle(Habbo habbo, String itemId) {
int rewardItemId = ItemDataGuard.parsePositiveInt(itemId);
if (habbo == null || habbo.getHabboInfo() == null || rewardItemId <= 0
|| Emulator.getGameEnvironment().getCatalogManager().ecotronItem == null) {
return null;
}
String extradata = Calendar.getInstance().get(Calendar.DAY_OF_MONTH) + "-" + (Calendar.getInstance().get(Calendar.MONTH) + 1) + "-" + Calendar.getInstance().get(Calendar.YEAR);
HabboItem item = null;
@@ -686,7 +703,7 @@ public class ItemManager {
try (PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO items_presents (item_id, base_item_reward) VALUES (?, ?)")) {
while (set.next() && item == null) {
preparedStatement.setInt(1, set.getInt(1));
preparedStatement.setInt(2, Integer.parseInt(itemId));
preparedStatement.setInt(2, rewardItemId);
preparedStatement.addBatch();
item = new InteractionDefault(set.getInt(1), habbo.getHabboInfo().getId(), Emulator.getGameEnvironment().getCatalogManager().ecotronItem, extradata, 0, 0);
}
@@ -829,6 +846,10 @@ public class ItemManager {
}
public HabboItem createGift(String username, Item item, String extraData, int limitedStack, int limitedSells) {
if (username == null || username.isBlank() || item == null) {
return null;
}
Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(username);
int userId = 0;
@@ -857,13 +878,13 @@ public class ItemManager {
}
public HabboItem createGift(int userId, Item item, String extraData, int limitedStack, int limitedSells) {
if (userId == 0)
if (userId <= 0 || item == null)
return null;
if (extraData.length() > 1000) {
if (extraData != null && extraData.length() > ItemDataGuard.MAX_EXTRA_DATA_LENGTH) {
LOGGER.error("Extradata exceeds maximum length of 1000 characters: {}", extraData);
extraData = extraData.substring(0, 1000);
}
extraData = ItemDataGuard.normalizeExtraData(extraData);
HabboItem gift = this.createItem(userId, item, limitedStack, limitedSells, extraData);
@@ -879,7 +900,7 @@ public class ItemManager {
}
public Item getItem(int itemId) {
if (itemId < 0)
if (itemId <= 0)
return null;
return this.items.get(itemId);
@@ -890,12 +911,16 @@ public class ItemManager {
}
public Item getItem(String itemName) {
if (itemName == null || itemName.isBlank()) {
return null;
}
TIntObjectIterator<Item> item = this.items.iterator();
for (int i = this.items.size(); i-- > 0; ) {
try {
item.advance();
if (item.value().getName().equalsIgnoreCase(itemName)) {
if (item.value() != null && item.value().getName() != null && item.value().getName().equalsIgnoreCase(itemName)) {
return item.value();
}
} catch (NoSuchElementException e) {
@@ -2,6 +2,7 @@ package com.eu.habbo.habbohotel.items.interactions;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredInputGuard;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -230,39 +231,18 @@ public abstract class InteractionWired extends InteractionDefault {
public static WiredSettings readSettings(ClientMessage packet, boolean isEffect)
{
int intParamCount = packet.readInt();
if (intParamCount < 0 || intParamCount > 100) {
throw new IllegalArgumentException("Invalid intParamCount: " + intParamCount);
}
int[] intParams = new int[intParamCount];
for(int i = 0; i < intParamCount; i++)
{
intParams[i] = packet.readInt();
}
String stringParam = packet.readString();
int itemCount = packet.readInt();
int selectionLimit = Emulator.getConfig() != null ? Emulator.getConfig().getInt("hotel.wired.furni.selection.count", 5) : 5;
if (itemCount < 0 || itemCount > selectionLimit * 20) {
throw new IllegalArgumentException("Invalid itemCount: " + itemCount + " exceeds maximum allowed limit");
}
int[] itemIds = new int[itemCount];
for(int i = 0; i < itemCount; i++)
{
itemIds[i] = packet.readInt();
}
int[] intParams = WiredInputGuard.readIntParams(packet);
String stringParam = WiredInputGuard.readStringParam(packet);
int[] itemIds = WiredInputGuard.readFurniIds(packet);
WiredSettings settings = new WiredSettings(intParams, stringParam, itemIds, -1);
if(isEffect)
{
settings.setDelay(packet.readInt());
settings.setDelay(WiredInputGuard.normalizeDelay(packet.readInt()));
}
settings.setStuffTypeSelectionCode(packet.readInt());
settings.setStuffTypeSelectionCode(WiredInputGuard.normalizeStuffSelectionCode(packet.readInt()));
return settings;
}
}
@@ -0,0 +1,90 @@
package com.eu.habbo.habbohotel.items.interactions.wired;
import com.eu.habbo.Emulator;
import com.eu.habbo.messages.ClientMessage;
import java.util.Arrays;
public final class WiredInputGuard {
public static final int MAX_INT_PARAMS = 100;
public static final int MAX_STRING_PARAM_LENGTH = 1024;
public static final int MAX_ABSOLUTE_FURNI_IDS = 100;
public static final int DEFAULT_MAX_DELAY = 20;
public static final int MAX_ABSOLUTE_DELAY = 3600;
public static final int MIN_STUFF_SELECTION_CODE = -1;
public static final int MAX_STUFF_SELECTION_CODE = 2;
private WiredInputGuard() {
}
public static int[] readIntParams(ClientMessage packet) {
int count = packet.readInt();
if (count < 0 || count > MAX_INT_PARAMS) {
throw new IllegalArgumentException("Invalid wired int param count");
}
int[] values = new int[count];
for (int i = 0; i < count; i++) {
values[i] = packet.readInt();
}
return values;
}
public static String readStringParam(ClientMessage packet) {
String value = packet.readString();
if (value == null || value.isEmpty()) {
return "";
}
return value.length() > MAX_STRING_PARAM_LENGTH
? value.substring(0, MAX_STRING_PARAM_LENGTH)
: value;
}
public static int[] readFurniIds(ClientMessage packet) {
int count = packet.readInt();
int maxCount = maxFurniSelectionCount();
if (count < 0 || count > maxCount) {
throw new IllegalArgumentException("Invalid wired furni selection count");
}
int[] values = new int[count];
int accepted = 0;
for (int i = 0; i < count; i++) {
int itemId = packet.readInt();
if (itemId > 0) {
values[accepted++] = itemId;
}
}
return accepted == values.length ? values : Arrays.copyOf(values, accepted);
}
public static int normalizeDelay(int delay) {
return Math.max(0, Math.min(delay, maxDelay()));
}
public static int normalizeStuffSelectionCode(int code) {
if (code < MIN_STUFF_SELECTION_CODE || code > MAX_STUFF_SELECTION_CODE) {
return MIN_STUFF_SELECTION_CODE;
}
return code;
}
public static int maxFurniSelectionCount() {
int selectionLimit = Emulator.getConfig() != null
? Emulator.getConfig().getInt("hotel.wired.furni.selection.count", 5)
: 5;
selectionLimit = Math.max(1, selectionLimit);
return Math.min(MAX_ABSOLUTE_FURNI_IDS, selectionLimit * 20);
}
public static int maxDelay() {
int configured = Emulator.getConfig() != null
? Emulator.getConfig().getInt("hotel.wired.max_delay", DEFAULT_MAX_DELAY)
: DEFAULT_MAX_DELAY;
configured = Math.max(0, configured);
return Math.min(MAX_ABSOLUTE_DELAY, configured);
}
}
@@ -0,0 +1,48 @@
package com.eu.habbo.habbohotel.items.interactions.wired;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.users.HabboItem;
import java.util.ArrayList;
import java.util.List;
public final class WiredLegacyDataGuard {
public static final int DEFAULT_MAX_DELAY = 20;
private WiredLegacyDataGuard() {
}
public static int parseDelay(String value) {
try {
int parsed = Integer.parseInt(value == null ? "" : value.trim());
return Math.max(0, Math.min(parsed, DEFAULT_MAX_DELAY));
} catch (NumberFormatException e) {
return 0;
}
}
public static List<HabboItem> parseRoomItems(String value, Room room) {
List<HabboItem> items = new ArrayList<>();
if (room == null || value == null || value.isBlank()) {
return items;
}
for (String part : value.split(";")) {
try {
int itemId = Integer.parseInt(part.trim());
if (itemId <= 0) {
continue;
}
HabboItem item = room.getHabboItem(itemId);
if (item != null) {
items.add(item);
}
} catch (NumberFormatException e) {
// Ignore malformed legacy ids and keep loading the remaining items.
}
}
return items;
}
}
@@ -0,0 +1,38 @@
package com.eu.habbo.habbohotel.items.interactions.wired;
import com.eu.habbo.Emulator;
public final class WiredNumericInputGuard {
public static final int DEFAULT_MAX_REWARD_AMOUNT = 1000;
public static final int DEFAULT_MAX_RESPECT_AMOUNT = 100;
public static final int MAX_ABSOLUTE_AMOUNT = 100000;
private WiredNumericInputGuard() {
}
public static int parsePositiveAmount(String value, int maxAmount) {
try {
int parsed = Integer.parseInt(value == null ? "" : value.trim());
if (parsed <= 0) {
return 0;
}
return Math.min(parsed, Math.max(1, Math.min(maxAmount, MAX_ABSOLUTE_AMOUNT)));
} catch (NumberFormatException e) {
return 0;
}
}
public static int maxRewardAmount() {
return configuredMax("hotel.wired.reward.max_amount", DEFAULT_MAX_REWARD_AMOUNT);
}
public static int maxRespectAmount() {
return configuredMax("hotel.wired.respect.max_amount", DEFAULT_MAX_RESPECT_AMOUNT);
}
private static int configuredMax(String key, int fallback) {
int configured = Emulator.getConfig() != null ? Emulator.getConfig().getInt(key, fallback) : fallback;
return Math.max(1, Math.min(configured, MAX_ABSOLUTE_AMOUNT));
}
}
@@ -0,0 +1,45 @@
package com.eu.habbo.habbohotel.items.interactions.wired;
public final class WiredTimerInputGuard {
public static final int MAX_TIMER_MS = 24 * 60 * 60 * 1000;
private WiredTimerInputGuard() {
}
public static int fromClientUnits(int units, int stepMs, int minMs) {
return fromClientUnits(units, stepMs, minMs, MAX_TIMER_MS);
}
public static int fromClientUnits(int units, int stepMs, int minMs, int maxMs) {
if (units < 1 || stepMs < 1) {
return minMs;
}
long value = (long) units * stepMs;
return clamp(value, minMs, maxMs);
}
public static int normalizeStoredMillis(Integer millis, int minMs, int fallbackMs) {
return normalizeStoredMillis(millis, minMs, fallbackMs, MAX_TIMER_MS);
}
public static int normalizeStoredMillis(Integer millis, int minMs, int fallbackMs, int maxMs) {
if (millis == null || millis < minMs) {
return fallbackMs;
}
return clamp(millis.longValue(), minMs, maxMs);
}
private static int clamp(long value, int minMs, int maxMs) {
if (value < minMs) {
return minMs;
}
if (value > maxMs) {
return maxMs;
}
return (int) value;
}
}
@@ -111,7 +111,13 @@ public class WiredConditionActorDir extends InteractionWiredCondition {
}
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
JsonData data;
try {
data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
} catch (RuntimeException exception) {
this.onPickUp();
return;
}
if (data == null) {
return;
@@ -157,15 +163,15 @@ public class WiredConditionActorDir extends InteractionWiredCondition {
return (this.directionMask & (1 << direction)) != 0;
}
private int normalizeDirectionMask(int value) {
int normalizeDirectionMask(int value) {
return value & ALL_DIRECTIONS_MASK;
}
private int normalizeUserSource(int value) {
int normalizeUserSource(int value) {
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
}
private int normalizeQuantifier(int value) {
int normalizeQuantifier(int value) {
return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
}
@@ -111,19 +111,21 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.items.clear();
this.comparison = COMPARISON_EQUAL;
this.minutes = 0;
this.halfSecondSteps = 0;
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = QUANTIFIER_ALL;
this.resetSettings();
String wiredData = set.getString("wired_data");
if (wiredData == null || wiredData.isEmpty() || !wiredData.startsWith("{")) {
return;
}
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
JsonData data;
try {
data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
} catch (RuntimeException exception) {
this.resetSettings();
return;
}
if (data == null) {
return;
}
@@ -131,7 +133,7 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition
this.comparison = this.normalizeComparison(data.comparison);
this.minutes = this.normalizeMinutes(data.minutes);
this.halfSecondSteps = this.normalizeHalfSecondSteps(data.halfSecondSteps);
this.furniSource = data.furniSource;
this.furniSource = this.normalizeFurniSource(data.furniSource);
this.quantifier = this.normalizeQuantifier(data.quantifier);
if (data.itemIds == null) {
@@ -139,6 +141,10 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition
}
for (Integer id : data.itemIds) {
if (id == null) {
continue;
}
HabboItem item = room.getHabboItem(id);
if (item instanceof InteractionGameUpCounter) {
this.items.add(item);
@@ -195,7 +201,7 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition
this.comparison = (params.length > 0) ? this.normalizeComparison(params[0]) : COMPARISON_EQUAL;
this.minutes = (params.length > 1) ? this.normalizeMinutes(params[1]) : 0;
this.halfSecondSteps = (params.length > 2) ? this.normalizeHalfSecondSteps(params[2]) : 0;
this.furniSource = (params.length > 3) ? params[3] : WiredSourceUtil.SOURCE_TRIGGER;
this.furniSource = (params.length > 3) ? this.normalizeFurniSource(params[3]) : WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = (params.length > 4) ? this.normalizeQuantifier(params[4]) : QUANTIFIER_ALL;
this.items.clear();
@@ -224,6 +230,15 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition
return true;
}
private void resetSettings() {
this.items.clear();
this.comparison = COMPARISON_EQUAL;
this.minutes = 0;
this.halfSecondSteps = 0;
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = QUANTIFIER_ALL;
}
private void refresh(Room room) {
THashSet<HabboItem> remove = new THashSet<>();
@@ -256,7 +271,7 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition
}
}
private int normalizeComparison(int value) {
int normalizeComparison(int value) {
if (value < COMPARISON_LESS || value > COMPARISON_GREATER) {
return COMPARISON_EQUAL;
}
@@ -264,18 +279,30 @@ public class WiredConditionCounterTimeMatches extends InteractionWiredCondition
return value;
}
private int normalizeMinutes(int value) {
int normalizeMinutes(int value) {
return Math.max(0, Math.min(MAX_MINUTES, value));
}
private int normalizeHalfSecondSteps(int value) {
int normalizeHalfSecondSteps(int value) {
return Math.max(0, Math.min(MAX_HALF_SECOND_STEPS, value));
}
private int normalizeQuantifier(int value) {
int normalizeQuantifier(int value) {
return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
}
int normalizeFurniSource(int value) {
switch (value) {
case WiredSourceUtil.SOURCE_SELECTED:
case WiredSourceUtil.SOURCE_SELECTOR:
case WiredSourceUtil.SOURCE_SIGNAL:
case WiredSourceUtil.SOURCE_TRIGGER:
return value;
default:
return WiredSourceUtil.SOURCE_TRIGGER;
}
}
static class JsonData {
int comparison;
int minutes;
@@ -53,8 +53,7 @@ public class WiredConditionDateRangeActive extends InteractionWiredCondition {
@Override
public boolean saveData(WiredSettings settings) {
if(settings.getIntParams().length < 2) return false;
this.startDate = settings.getIntParams()[0];
this.endDate = settings.getIntParams()[1];
this.applyRange(settings.getIntParams()[0], settings.getIntParams()[1]);
return true;
}
@@ -81,19 +80,26 @@ public class WiredConditionDateRangeActive extends InteractionWiredCondition {
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
String wiredData = set.getString("wired_data");
if (wiredData == null || wiredData.isEmpty()) {
this.applyRange(0, 0);
return;
}
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.startDate = data.startDate;
this.endDate = data.endDate;
if (data == null) {
this.applyRange(0, 0);
return;
}
this.applyRange(data.startDate, data.endDate);
} else {
String[] data = wiredData.split("\t");
if (data.length == 2) {
try {
this.startDate = Integer.parseInt(data[0]);
this.endDate = Integer.parseInt(data[1]);
this.applyRange(Integer.parseInt(data[0]), Integer.parseInt(data[1]));
} catch (Exception e) {
this.applyRange(0, 0);
}
}
}
@@ -105,6 +111,12 @@ public class WiredConditionDateRangeActive extends InteractionWiredCondition {
this.endDate = 0;
}
private void applyRange(int startDate, int endDate) {
int[] range = WiredDateRangeInputGuard.normalizeRange(startDate, endDate);
this.startDate = range[0];
this.endDate = range[1];
}
static class JsonData {
int startDate;
int endDate;
@@ -92,21 +92,37 @@ public class WiredConditionFurniHaveFurni extends InteractionWiredCondition {
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.onPickUp();
String wiredData = set.getString("wired_data");
if (wiredData == null || wiredData.isEmpty()) {
return;
}
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.all = data.all;
this.furniSource = data.furniSource;
for(int id : data.itemIds) {
HabboItem item = room.getHabboItem(id);
if (item != null) {
this.items.add(item);
}
JsonData data;
try {
data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
} catch (RuntimeException exception) {
this.onPickUp();
return;
}
if (data == null) {
return;
}
this.all = data.all;
this.furniSource = WiredFurniConditionInputGuard.normalizeFurniSource(data.furniSource);
for(int id : WiredFurniConditionInputGuard.sanitizeItemIds(data.itemIds, WiredManager.MAXIMUM_FURNI_SELECTION)) {
HabboItem item = room.getHabboItem(id);
HabboItem item = room.getHabboItem(id);
if (item != null) {
this.items.add(item);
}
}
}
} else {
String[] data = wiredData.split(":");
@@ -114,21 +130,19 @@ public class WiredConditionFurniHaveFurni extends InteractionWiredCondition {
this.all = (data[0].equals("1"));
if (data.length == 2) {
String[] items = data[1].split(";");
for (int id : WiredFurniConditionInputGuard.parseLegacyItemIds(data[1], WiredManager.MAXIMUM_FURNI_SELECTION)) {
HabboItem item = room.getHabboItem(id);
for (String s : items) {
HabboItem item = room.getHabboItem(Integer.parseInt(s));
if (item != null)
this.items.add(item);
if (item != null)
this.items.add(item);
} catch (NumberFormatException ignored) {
}
}
}
}
this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED;
}
if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) {
this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
}
this.furniSource = WiredFurniConditionInputGuard.selectedOrNormalizedFurniSource(this.furniSource, !this.items.isEmpty());
}
@Override
@@ -172,14 +186,12 @@ public class WiredConditionFurniHaveFurni extends InteractionWiredCondition {
int[] params = settings.getIntParams();
this.all = params[0] == 1;
this.furniSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER;
this.furniSource = (params.length > 1) ? WiredFurniConditionInputGuard.normalizeFurniSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER;
int count = settings.getFurniIds().length;
if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) return false;
if (count > 0 && this.furniSource == WiredSourceUtil.SOURCE_TRIGGER) {
this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
}
this.furniSource = WiredFurniConditionInputGuard.selectedOrNormalizedFurniSource(this.furniSource, count > 0);
this.items.clear();
@@ -198,6 +210,18 @@ public class WiredConditionFurniHaveFurni extends InteractionWiredCondition {
return true;
}
int normalizeFurniSource(int value) {
switch (value) {
case WiredSourceUtil.SOURCE_SELECTED:
case WiredSourceUtil.SOURCE_SELECTOR:
case WiredSourceUtil.SOURCE_SIGNAL:
case WiredSourceUtil.SOURCE_TRIGGER:
return value;
default:
return WiredSourceUtil.SOURCE_TRIGGER;
}
}
private void refresh() {
THashSet<HabboItem> items = new THashSet<>();
@@ -89,41 +89,43 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition {
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.items.clear();
this.onPickUp();
String wiredData = set.getString("wired_data");
if (wiredData == null || wiredData.isEmpty()) {
return;
}
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.furniSource = data.furniSource;
this.furniSource = WiredFurniConditionInputGuard.normalizeFurniSource(data.furniSource);
this.all = data.all;
for(int id : data.itemIds) {
for(int id : WiredFurniConditionInputGuard.sanitizeItemIds(data.itemIds, WiredManager.MAXIMUM_FURNI_SELECTION)) {
HabboItem item = room.getHabboItem(id);
if (item != null) {
this.items.add(item);
HabboItem item = room.getHabboItem(id);
if (item != null) {
this.items.add(item);
}
}
}
} else {
String[] data = wiredData.split(":");
if (data.length >= 1) {
if (data.length >= 2) {
for (int id : WiredFurniConditionInputGuard.parseLegacyItemIds(data[1], WiredManager.MAXIMUM_FURNI_SELECTION)) {
HabboItem item = room.getHabboItem(id);
String[] items = data[1].split(";");
for (String s : items) {
HabboItem item = room.getHabboItem(Integer.parseInt(s));
if (item != null)
this.items.add(item);
if (item != null)
this.items.add(item);
} catch (NumberFormatException ignored) {
}
}
}
this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED;
this.all = false;
}
if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) {
this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
}
this.furniSource = WiredFurniConditionInputGuard.selectedOrNormalizedFurniSource(this.furniSource, !this.items.isEmpty());
}
@Override
@@ -162,11 +164,9 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition {
int[] params = settings.getIntParams();
this.all = (params.length > 0) && (params[0] == 1);
this.furniSource = (params.length > 1) ? params[1] : ((params.length > 0 && params[0] > 1) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER);
this.furniSource = (params.length > 1) ? WiredFurniConditionInputGuard.normalizeFurniSource(params[1]) : ((params.length > 0 && params[0] > 1) ? WiredFurniConditionInputGuard.normalizeFurniSource(params[0]) : WiredSourceUtil.SOURCE_TRIGGER);
if (count > 0 && this.furniSource == WiredSourceUtil.SOURCE_TRIGGER) {
this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
}
this.furniSource = WiredFurniConditionInputGuard.selectedOrNormalizedFurniSource(this.furniSource, count > 0);
this.items.clear();
@@ -186,6 +186,18 @@ public class WiredConditionFurniHaveHabbo extends InteractionWiredCondition {
return true;
}
int normalizeFurniSource(int value) {
switch (value) {
case WiredSourceUtil.SOURCE_SELECTED:
case WiredSourceUtil.SOURCE_SELECTOR:
case WiredSourceUtil.SOURCE_SIGNAL:
case WiredSourceUtil.SOURCE_TRIGGER:
return value;
default:
return WiredSourceUtil.SOURCE_TRIGGER;
}
}
protected boolean hasAvatarOnItem(HabboItem item, Room room, Collection<Habbo> habbos, Collection<Bot> bots, Collection<Pet> pets) {
RoomTile baseTile = room.getLayout().getTile(item.getX(), item.getY());
if (baseTile == null) return false;
@@ -16,6 +16,7 @@ import java.sql.SQLException;
public class WiredConditionHabboCount extends InteractionWiredCondition {
public static final WiredConditionType type = WiredConditionType.USER_COUNT;
static final int MAX_USER_COUNT_LIMIT = 1000;
private int lowerLimit = 0;
private int upperLimit = 50;
@@ -31,6 +32,10 @@ public class WiredConditionHabboCount extends InteractionWiredCondition {
@Override
public boolean evaluate(WiredContext ctx) {
if (ctx == null || ctx.room() == null) {
return false;
}
int count = (this.userSource == WiredSourceUtil.SOURCE_TRIGGER)
? ctx.room().getUserCount()
: WiredSourceUtil.resolveUsers(ctx, this.userSource).size();
@@ -55,26 +60,29 @@ public class WiredConditionHabboCount extends InteractionWiredCondition {
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.onPickUp();
String wiredData = set.getString("wired_data");
if (wiredData == null || wiredData.isEmpty()) {
return;
}
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.lowerLimit = data.lowerLimit;
this.upperLimit = data.upperLimit;
this.userSource = data.userSource;
this.applyLimits(data.lowerLimit, data.upperLimit);
this.userSource = WiredConditionInputGuard.normalizeUserSource(data.userSource);
} else {
String[] data = wiredData.split(":");
if (data.length >= 2) {
try {
this.lowerLimit = Integer.parseInt(data[0].trim());
this.upperLimit = Integer.parseInt(data[1].trim());
this.applyLimits(Integer.parseInt(data[0].trim()), Integer.parseInt(data[1].trim()));
} catch (NumberFormatException ignored) {
// malformed legacy data — keep the constructed defaults
}
}
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
@Override
@@ -110,14 +118,19 @@ public class WiredConditionHabboCount extends InteractionWiredCondition {
@Override
public boolean saveData(WiredSettings settings) {
if(settings.getIntParams().length < 2) return false;
this.lowerLimit = settings.getIntParams()[0];
this.upperLimit = settings.getIntParams()[1];
int[] params = settings.getIntParams();
this.userSource = (params.length > 2) ? params[2] : WiredSourceUtil.SOURCE_TRIGGER;
this.applyLimits(params[0], params[1]);
this.userSource = (params.length > 2) ? WiredConditionInputGuard.normalizeUserSource(params[2]) : WiredSourceUtil.SOURCE_TRIGGER;
return true;
}
private void applyLimits(int lowerLimit, int upperLimit) {
int[] limits = WiredConditionInputGuard.normalizeUserCountRange(lowerLimit, upperLimit);
this.lowerLimit = limits[0];
this.upperLimit = limits[1];
}
static class JsonData {
int lowerLimit;
int upperLimit;
@@ -18,6 +18,7 @@ import java.util.List;
public class WiredConditionHabboHasEffect extends InteractionWiredCondition {
protected static final int QUANTIFIER_ALL = 0;
protected static final int QUANTIFIER_ANY = 1;
protected static final int MAX_EFFECT_ID = 10_000;
public static final WiredConditionType type = WiredConditionType.ACTOR_WEARS_EFFECT;
@@ -86,15 +87,34 @@ public class WiredConditionHabboHasEffect extends InteractionWiredCondition {
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.onPickUp();
String wiredData = set.getString("wired_data");
if (wiredData == null || wiredData.isEmpty()) {
this.onPickUp();
return;
}
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.effectId = data.effectId;
this.userSource = data.userSource;
JsonData data;
try {
data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
} catch (RuntimeException ignored) {
this.onPickUp();
return;
}
if (data == null) {
this.onPickUp();
return;
}
this.effectId = WiredUserConditionInputGuard.normalizeEffectId(data.effectId);
this.userSource = WiredUserConditionInputGuard.normalizeUserSource(data.userSource);
this.quantifier = this.normalizeQuantifier(data.quantifier, QUANTIFIER_ANY);
} else {
this.effectId = Integer.parseInt(wiredData);
try {
this.effectId = WiredUserConditionInputGuard.normalizeEffectId(Integer.parseInt(wiredData));
} catch (NumberFormatException ignored) {
this.effectId = 0;
}
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = QUANTIFIER_ANY;
}
@@ -134,8 +154,8 @@ public class WiredConditionHabboHasEffect extends InteractionWiredCondition {
public boolean saveData(WiredSettings settings) {
if(settings.getIntParams().length < 1) return false;
int[] params = settings.getIntParams();
this.effectId = params[0];
this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER;
this.effectId = WiredUserConditionInputGuard.normalizeEffectId(params[0]);
this.userSource = (params.length > 1) ? WiredUserConditionInputGuard.normalizeUserSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2], QUANTIFIER_ANY) : QUANTIFIER_ANY;
return true;
@@ -153,6 +173,14 @@ public class WiredConditionHabboHasEffect extends InteractionWiredCondition {
return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
}
protected int normalizeEffectId(int value) {
return Math.max(0, Math.min(MAX_EFFECT_ID, value));
}
protected int normalizeUserSource(int value) {
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
}
static class JsonData {
int effectId;
int userSource;
@@ -10,17 +10,15 @@ import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.core.WiredContext;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import com.eu.habbo.messages.ServerMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class WiredConditionHabboHasHandItem extends InteractionWiredCondition {
private static final Logger LOGGER = LoggerFactory.getLogger(WiredConditionHabboHasHandItem.class);
protected static final int QUANTIFIER_ALL = 0;
protected static final int QUANTIFIER_ANY = 1;
protected static final int MAX_HAND_ITEM_ID = 10_000;
public static final WiredConditionType type = WiredConditionType.ACTOR_HAS_HANDITEM;
@@ -62,9 +60,9 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition {
@Override
public boolean saveData(WiredSettings settings) {
if(settings.getIntParams().length < 1) return false;
this.handItem = this.normalizeHandItem(settings.getIntParams()[0]);
this.handItem = WiredUserConditionInputGuard.normalizeHandItemId(settings.getIntParams()[0]);
int[] params = settings.getIntParams();
this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER;
this.userSource = (params.length > 1) ? WiredUserConditionInputGuard.normalizeUserSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2]) : QUANTIFIER_ALL;
return true;
@@ -99,21 +97,35 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition {
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
try {
String wiredData = set.getString("wired_data");
String wiredData = set.getString("wired_data");
if (wiredData == null || wiredData.isEmpty()) {
this.onPickUp();
return;
}
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.handItem = this.normalizeHandItem(data.handItemId);
this.userSource = data.userSource;
this.quantifier = this.normalizeQuantifier(data.quantifier);
} else {
this.handItem = this.normalizeHandItem(Integer.parseInt(wiredData));
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = QUANTIFIER_ALL;
if (wiredData.startsWith("{")) {
JsonData data;
try {
data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
} catch (RuntimeException ignored) {
this.onPickUp();
return;
}
} catch (Exception e) {
LOGGER.error("Caught exception", e);
if (data == null) {
this.onPickUp();
return;
}
this.handItem = WiredUserConditionInputGuard.normalizeHandItemId(data.handItemId);
this.userSource = WiredUserConditionInputGuard.normalizeUserSource(data.userSource);
this.quantifier = this.normalizeQuantifier(data.quantifier);
} else {
try {
this.handItem = WiredUserConditionInputGuard.normalizeHandItemId(Integer.parseInt(wiredData));
} catch (NumberFormatException ignored) {
this.handItem = 0;
}
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = QUANTIFIER_ALL;
}
}
@@ -156,14 +168,14 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition {
return true;
}
protected int normalizeHandItem(int value) {
return Math.max(0, value);
}
protected int normalizeQuantifier(int value) {
return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
}
protected int normalizeUserSource(int value) {
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
}
static class JsonData {
int handItemId;
int userSource;
@@ -20,6 +20,7 @@ import java.util.List;
public class WiredConditionHabboWearsBadge extends InteractionWiredCondition {
protected static final int QUANTIFIER_ALL = 0;
protected static final int QUANTIFIER_ANY = 1;
protected static final int MAX_BADGE_CODE_LENGTH = 64;
public static final WiredConditionType type = WiredConditionType.ACTOR_WEARS_BADGE;
@@ -37,6 +38,10 @@ public class WiredConditionHabboWearsBadge extends InteractionWiredCondition {
@Override
public boolean evaluate(WiredContext ctx) {
if (ctx == null || ctx.room() == null) {
return false;
}
Room room = ctx.room();
List<RoomUnit> targets = WiredSourceUtil.resolveUsers(ctx, this.userSource);
if (targets.isEmpty()) return false;
@@ -102,15 +107,30 @@ public class WiredConditionHabboWearsBadge extends InteractionWiredCondition {
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.onPickUp();
String wiredData = set.getString("wired_data");
if (wiredData == null) {
this.onPickUp();
return;
}
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.badge = data.badge;
this.userSource = data.userSource;
JsonData data;
try {
data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
} catch (RuntimeException ignored) {
this.onPickUp();
return;
}
if (data == null) {
this.onPickUp();
return;
}
this.badge = WiredUserConditionInputGuard.normalizeBadgeCode(data.badge);
this.userSource = WiredUserConditionInputGuard.normalizeUserSource(data.userSource);
this.quantifier = this.normalizeQuantifier(data.quantifier, QUANTIFIER_ANY);
} else {
this.badge = wiredData;
this.badge = WiredUserConditionInputGuard.normalizeBadgeCode(wiredData);
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = QUANTIFIER_ANY;
}
@@ -147,9 +167,9 @@ public class WiredConditionHabboWearsBadge extends InteractionWiredCondition {
@Override
public boolean saveData(WiredSettings settings) {
this.badge = settings.getStringParam();
this.badge = WiredUserConditionInputGuard.normalizeBadgeCode(settings.getStringParam());
int[] params = settings.getIntParams();
this.userSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER;
this.userSource = (params.length > 0) ? WiredUserConditionInputGuard.normalizeUserSource(params[0]) : WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = (params.length > 1) ? this.normalizeQuantifier(params[1], QUANTIFIER_ANY) : QUANTIFIER_ANY;
return true;
@@ -167,6 +187,19 @@ public class WiredConditionHabboWearsBadge extends InteractionWiredCondition {
return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
}
protected String normalizeBadge(String value) {
if (value == null) {
return "";
}
String normalized = value.trim();
return normalized.length() <= MAX_BADGE_CODE_LENGTH ? normalized : normalized.substring(0, MAX_BADGE_CODE_LENGTH);
}
protected int normalizeUserSource(int value) {
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
}
static class JsonData {
String badge;
int userSource;
@@ -97,7 +97,14 @@ public class WiredConditionHasAltitude extends InteractionWiredCondition {
return;
}
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
JsonData data;
try {
data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
} catch (RuntimeException exception) {
this.onPickUp();
return;
}
if (data == null) {
return;
}
@@ -112,6 +119,10 @@ public class WiredConditionHasAltitude extends InteractionWiredCondition {
}
for (Integer id : data.itemIds) {
if (id == null) {
continue;
}
HabboItem item = room.getHabboItem(id);
if (item != null) {
this.items.add(item);
@@ -225,7 +236,7 @@ public class WiredConditionHasAltitude extends InteractionWiredCondition {
}
}
private int normalizeComparison(int value) {
int normalizeComparison(int value) {
if (value < COMPARISON_LESS || value > COMPARISON_GREATER) {
return COMPARISON_EQUAL;
}
@@ -233,11 +244,11 @@ public class WiredConditionHasAltitude extends InteractionWiredCondition {
return value;
}
private int normalizeQuantifier(int value) {
int normalizeQuantifier(int value) {
return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
}
private int normalizeFurniSource(int value) {
int normalizeFurniSource(int value) {
switch (value) {
case WiredSourceUtil.SOURCE_SELECTED:
case WiredSourceUtil.SOURCE_SELECTOR:
@@ -249,12 +260,12 @@ public class WiredConditionHasAltitude extends InteractionWiredCondition {
}
}
private double normalizeAltitude(double value) {
double normalizeAltitude(double value) {
double clampedValue = Math.max(0.0D, Math.min(Room.MAXIMUM_FURNI_HEIGHT, value));
return BigDecimal.valueOf(clampedValue).setScale(2, RoundingMode.HALF_UP).doubleValue();
}
private double parseAltitudeOrDefault(String value) {
double parseAltitudeOrDefault(String value) {
if (value == null || value.trim().isEmpty()) {
return 0.0D;
}
@@ -266,7 +277,7 @@ public class WiredConditionHasAltitude extends InteractionWiredCondition {
}
}
private String formatAltitude(double value) {
String formatAltitude(double value) {
BigDecimal decimal = BigDecimal.valueOf(this.normalizeAltitude(value)).stripTrailingZeros();
return (decimal.scale() < 0 ? decimal.setScale(0, RoundingMode.DOWN) : decimal).toPlainString();
}
@@ -0,0 +1,57 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.games.GameTeamColors;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
public final class WiredConditionInputGuard {
public static final int MAX_USER_COUNT_LIMIT = 1000;
public static final int MAX_TIMER_CYCLES = 24 * 60 * 60 * 2;
private WiredConditionInputGuard() {
}
public static GameTeamColors normalizeTeamColor(GameTeamColors value, GameTeamColors fallback) {
return (value != null) ? value : fallback;
}
public static GameTeamColors normalizeTeamColorType(int value, GameTeamColors fallback) {
for (GameTeamColors color : GameTeamColors.values()) {
if (color.type == value) {
return color;
}
}
return fallback;
}
public static int normalizeUserSource(int value) {
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
}
public static int normalizeTimerCycles(int value) {
if (value < 0) {
return 0;
}
return Math.min(value, MAX_TIMER_CYCLES);
}
public static int[] normalizeUserCountRange(int lowerLimit, int upperLimit) {
int lower = clampUserCount(lowerLimit);
int upper = clampUserCount(upperLimit);
if (lower > upper) {
return new int[]{upper, lower};
}
return new int[]{lower, upper};
}
private static int clampUserCount(int value) {
if (value < 0) {
return 0;
}
return Math.min(value, MAX_USER_COUNT_LIMIT);
}
}
@@ -52,12 +52,13 @@ public class WiredConditionLessTimeElapsed extends InteractionWiredCondition {
try {
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.cycles = data.cycles;
this.cycles = WiredConditionInputGuard.normalizeTimerCycles(data.cycles);
} else {
if (!wiredData.equals(""))
this.cycles = Integer.parseInt(wiredData);
this.cycles = WiredConditionInputGuard.normalizeTimerCycles(Integer.parseInt(wiredData));
}
} catch (Exception e) {
this.cycles = 0;
}
}
@@ -90,10 +91,14 @@ public class WiredConditionLessTimeElapsed extends InteractionWiredCondition {
@Override
public boolean saveData(WiredSettings settings) {
if(settings.getIntParams().length < 1) return false;
this.cycles = settings.getIntParams()[0];
this.cycles = WiredConditionInputGuard.normalizeTimerCycles(settings.getIntParams()[0]);
return true;
}
int normalizeCycles(int value) {
return Math.max(0, Math.min(WiredConditionMoreTimeElapsed.MAX_CYCLES, value));
}
static class JsonData {
int cycles;
@@ -87,7 +87,7 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition
this.direction = params[1] == 1;
this.position = params[2] == 1;
this.altitude = (params.length > 3) && (params[3] == 1);
this.furniSource = (params.length > 4) ? this.normalizeFurniSource(params[4]) : ((params.length > 3 && params[3] > 1) ? this.normalizeFurniSource(params[3]) : WiredSourceUtil.SOURCE_TRIGGER);
this.furniSource = (params.length > 4) ? WiredMatchPositionInputGuard.normalizeFurniSource(params[4], false) : ((params.length > 3 && params[3] > 1) ? WiredMatchPositionInputGuard.normalizeFurniSource(params[3], false) : WiredSourceUtil.SOURCE_TRIGGER);
this.quantifier = (params.length > 5) ? this.normalizeQuantifier(params[5]) : QUANTIFIER_ALL;
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
@@ -108,6 +108,8 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition
this.settings.add(new WiredMatchFurniSetting(item.getId(), item.getExtradata(), item.getRotation(), item.getX(), item.getY(), item.getZ()));
}
this.furniSource = WiredMatchPositionInputGuard.normalizeFurniSource(this.furniSource, !this.settings.isEmpty());
return true;
}
@@ -282,31 +284,22 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition
this.position = data.position;
this.direction = data.direction;
this.altitude = data.altitude;
if (data.settings != null) {
for (WiredMatchFurniSetting setting : data.settings) {
WiredMatchFurniSetting normalized = this.normalizeSetting(setting);
if (normalized != null) {
this.settings.add(normalized);
}
}
}
this.furniSource = this.normalizeFurniSource(data.furniSource);
this.settings.addAll(WiredMatchPositionInputGuard.sanitizeSettings(data.settings, room));
this.furniSource = WiredMatchPositionInputGuard.normalizeFurniSource(data.furniSource, !this.settings.isEmpty());
this.quantifier = this.normalizeQuantifier(data.quantifier);
} else {
String[] data = wiredData.split(":");
if (data.length >= 5) {
try {
int itemCount = Integer.parseInt(data[0]);
int itemCount = Math.min(Integer.parseInt(data[0]), WiredManager.MAXIMUM_FURNI_SELECTION);
String[] items = data[1].split(";");
for (int i = 0; i < itemCount && i < items.length; i++) {
String[] stuff = items[i].split("-");
WiredMatchFurniSetting parsed = this.parseLegacySetting(stuff);
if (parsed != null) {
this.settings.add(parsed);
WiredMatchFurniSetting setting = this.parseLegacySetting(items[i], room);
if (setting != null) {
this.settings.add(setting);
}
}
@@ -319,11 +312,33 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition
}
this.altitude = false;
this.furniSource = this.settings.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED;
this.furniSource = WiredMatchPositionInputGuard.normalizeFurniSource(WiredSourceUtil.SOURCE_TRIGGER, !this.settings.isEmpty());
this.quantifier = QUANTIFIER_ALL;
}
}
private WiredMatchFurniSetting parseLegacySetting(String value, Room room) {
String[] parts = value.split("-", 6);
if (parts.length < 5) {
return null;
}
try {
double z = (parts.length >= 6) ? Double.parseDouble(parts[5]) : 0.0D;
return WiredMatchPositionInputGuard.sanitizeParts(
Integer.parseInt(parts[0]),
parts[1],
Integer.parseInt(parts[2]),
Integer.parseInt(parts[3]),
Integer.parseInt(parts[4]),
z,
room
);
} catch (NumberFormatException ignored) {
return null;
}
}
@Override
public void onPickUp() {
this.settings.clear();
@@ -16,6 +16,7 @@ import java.sql.SQLException;
public class WiredConditionMoreTimeElapsed extends InteractionWiredCondition {
private static final WiredConditionType type = WiredConditionType.TIME_MORE_THAN;
static final int MAX_CYCLES = 1_000_000;
private int cycles;
@@ -52,12 +53,13 @@ public class WiredConditionMoreTimeElapsed extends InteractionWiredCondition {
try {
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.cycles = data.cycles;
this.cycles = WiredConditionInputGuard.normalizeTimerCycles(data.cycles);
} else {
if (!wiredData.equals(""))
this.cycles = Integer.parseInt(wiredData);
this.cycles = WiredConditionInputGuard.normalizeTimerCycles(Integer.parseInt(wiredData));
}
} catch (Exception e) {
this.cycles = 0;
}
}
@@ -90,10 +92,14 @@ public class WiredConditionMoreTimeElapsed extends InteractionWiredCondition {
@Override
public boolean saveData(WiredSettings settings) {
if(settings.getIntParams().length < 1) return false;
this.cycles = settings.getIntParams()[0];
this.cycles = WiredConditionInputGuard.normalizeTimerCycles(settings.getIntParams()[0]);
return true;
}
int normalizeCycles(int value) {
return Math.max(0, Math.min(MAX_CYCLES, value));
}
static class JsonData {
int cycles;
@@ -99,9 +99,9 @@ public class WiredConditionNotFurniHaveFurni extends InteractionWiredCondition {
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.all = data.all;
this.furniSource = data.furniSource;
this.furniSource = WiredFurniConditionInputGuard.normalizeFurniSource(data.furniSource);
for (int id : data.itemIds) {
for (int id : WiredFurniConditionInputGuard.sanitizeItemIds(data.itemIds, WiredManager.MAXIMUM_FURNI_SELECTION)) {
HabboItem item = room.getHabboItem(id);
if (item != null) {
@@ -115,10 +115,8 @@ public class WiredConditionNotFurniHaveFurni extends InteractionWiredCondition {
this.all = (data[0].equals("1"));
if (data.length == 2) {
String[] items = data[1].split(";");
for (String s : items) {
HabboItem item = room.getHabboItem(Integer.parseInt(s));
for (int id : WiredFurniConditionInputGuard.parseLegacyItemIds(data[1], WiredManager.MAXIMUM_FURNI_SELECTION)) {
HabboItem item = room.getHabboItem(id);
if (item != null)
this.items.add(item);
@@ -127,9 +125,7 @@ public class WiredConditionNotFurniHaveFurni extends InteractionWiredCondition {
}
this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED;
}
if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) {
this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
}
this.furniSource = WiredFurniConditionInputGuard.selectedOrNormalizedFurniSource(this.furniSource, !this.items.isEmpty());
}
@Override
@@ -172,14 +168,12 @@ public class WiredConditionNotFurniHaveFurni extends InteractionWiredCondition {
if(settings.getIntParams().length < 1) return false;
int[] params = settings.getIntParams();
this.all = params[0] == 1;
this.furniSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER;
this.furniSource = (params.length > 1) ? WiredFurniConditionInputGuard.normalizeFurniSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER;
int count = settings.getFurniIds().length;
if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) return false;
if (count > 0 && this.furniSource == WiredSourceUtil.SOURCE_TRIGGER) {
this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
}
this.furniSource = WiredFurniConditionInputGuard.selectedOrNormalizedFurniSource(this.furniSource, count > 0);
this.items.clear();
@@ -95,10 +95,10 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition {
if (wiredData.startsWith("{")) {
WiredConditionFurniHaveHabbo.JsonData data = WiredManager.getGson().fromJson(wiredData, WiredConditionFurniHaveHabbo.JsonData.class);
this.furniSource = data.furniSource;
this.furniSource = WiredFurniConditionInputGuard.normalizeFurniSource(data.furniSource);
this.all = data.all;
for(int id : data.itemIds) {
for(int id : WiredFurniConditionInputGuard.sanitizeItemIds(data.itemIds, WiredManager.MAXIMUM_FURNI_SELECTION)) {
HabboItem item = room.getHabboItem(id);
if (item != null) {
@@ -108,11 +108,9 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition {
} else {
String[] data = wiredData.split(":");
if (data.length >= 1) {
String[] items = data[1].split(";");
for (String s : items) {
HabboItem item = room.getHabboItem(Integer.parseInt(s));
if (data.length >= 2) {
for (int id : WiredFurniConditionInputGuard.parseLegacyItemIds(data[1], WiredManager.MAXIMUM_FURNI_SELECTION)) {
HabboItem item = room.getHabboItem(id);
if (item != null)
this.items.add(item);
@@ -121,9 +119,7 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition {
this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED;
this.all = false;
}
if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) {
this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
}
this.furniSource = WiredFurniConditionInputGuard.selectedOrNormalizedFurniSource(this.furniSource, !this.items.isEmpty());
}
@Override
@@ -161,11 +157,9 @@ public class WiredConditionNotFurniHaveHabbo extends InteractionWiredCondition {
int[] params = settings.getIntParams();
this.all = (params.length > 0) && (params[0] == 1);
this.furniSource = (params.length > 1) ? params[1] : ((params.length > 0 && params[0] > 1) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER);
this.furniSource = (params.length > 1) ? WiredFurniConditionInputGuard.normalizeFurniSource(params[1]) : ((params.length > 0 && params[0] > 1) ? WiredFurniConditionInputGuard.normalizeFurniSource(params[0]) : WiredSourceUtil.SOURCE_TRIGGER);
if (count > 0 && this.furniSource == WiredSourceUtil.SOURCE_TRIGGER) {
this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
}
this.furniSource = WiredFurniConditionInputGuard.selectedOrNormalizedFurniSource(this.furniSource, count > 0);
this.items.clear();
@@ -6,8 +6,8 @@ import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.wired.WiredConditionType;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.core.WiredContext;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import com.eu.habbo.messages.ServerMessage;
@@ -31,6 +31,10 @@ public class WiredConditionNotHabboCount extends InteractionWiredCondition {
@Override
public boolean evaluate(WiredContext ctx) {
if (ctx == null || ctx.room() == null) {
return false;
}
int count = (this.userSource == WiredSourceUtil.SOURCE_TRIGGER)
? ctx.room().getUserCount()
: WiredSourceUtil.resolveUsers(ctx, this.userSource).size();
@@ -55,31 +59,34 @@ public class WiredConditionNotHabboCount extends InteractionWiredCondition {
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.onPickUp();
String wiredData = set.getString("wired_data");
if (wiredData == null || wiredData.isEmpty()) {
return;
}
if (wiredData.startsWith("{")) {
WiredConditionHabboCount.JsonData data = WiredManager.getGson().fromJson(wiredData, WiredConditionHabboCount.JsonData.class);
this.lowerLimit = data.lowerLimit;
this.upperLimit = data.upperLimit;
this.userSource = data.userSource;
this.applyLimits(data.lowerLimit, data.upperLimit);
this.userSource = WiredConditionInputGuard.normalizeUserSource(data.userSource);
} else {
String[] data = wiredData.split(":");
if (data.length >= 2) {
try {
this.lowerLimit = Integer.parseInt(data[0].trim());
this.upperLimit = Integer.parseInt(data[1].trim());
this.applyLimits(Integer.parseInt(data[0].trim()), Integer.parseInt(data[1].trim()));
} catch (NumberFormatException ignored) {
// malformed legacy data keep the constructed defaults
}
}
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
@Override
public void onPickUp() {
this.upperLimit = 0;
this.lowerLimit = 20;
this.lowerLimit = 10;
this.upperLimit = 20;
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
@@ -109,14 +116,19 @@ public class WiredConditionNotHabboCount extends InteractionWiredCondition {
@Override
public boolean saveData(WiredSettings settings) {
if(settings.getIntParams().length < 2) return false;
this.lowerLimit = settings.getIntParams()[0];
this.upperLimit = settings.getIntParams()[1];
int[] params = settings.getIntParams();
this.userSource = (params.length > 2) ? params[2] : WiredSourceUtil.SOURCE_TRIGGER;
this.applyLimits(params[0], params[1]);
this.userSource = (params.length > 2) ? WiredConditionInputGuard.normalizeUserSource(params[2]) : WiredSourceUtil.SOURCE_TRIGGER;
return true;
}
private void applyLimits(int lowerLimit, int upperLimit) {
int[] limits = WiredConditionInputGuard.normalizeUserCountRange(lowerLimit, upperLimit);
this.lowerLimit = limits[0];
this.upperLimit = limits[1];
}
static class JsonData {
int lowerLimit;
int upperLimit;
@@ -29,6 +29,7 @@ abstract class WiredConditionTeamGameBase extends InteractionWiredCondition {
protected static final int COMPARISON_EQUAL = 1;
protected static final int COMPARISON_HIGHER = 2;
protected static final int TEAM_TRIGGERER = 0;
protected static final int MAX_SCORE = 1_000_000;
private static final GameTeamColors[] SUPPORTED_TEAM_COLORS = new GameTeamColors[] {
GameTeamColors.RED,
@@ -96,7 +97,11 @@ abstract class WiredConditionTeamGameBase extends InteractionWiredCondition {
}
protected int normalizeScore(int value) {
return Math.max(0, value);
if (value < 0) {
return 0;
}
return Math.min(value, MAX_SCORE);
}
protected int normalizeExplicitTeamType(int value) {
@@ -65,7 +65,13 @@ public class WiredConditionTeamHasRank extends WiredConditionTeamGameBase {
return;
}
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
JsonData data;
try {
data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
} catch (RuntimeException ignored) {
this.resetSettings();
return;
}
if (data == null) {
return;
}
@@ -66,7 +66,13 @@ public class WiredConditionTeamHasScore extends WiredConditionTeamGameBase {
return;
}
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
JsonData data;
try {
data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
} catch (RuntimeException ignored) {
this.resetSettings();
return;
}
if (data == null) {
return;
}
@@ -97,12 +97,12 @@ public class WiredConditionTeamMember extends InteractionWiredCondition {
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.teamColor = data.teamColor;
this.userSource = data.userSource;
this.teamColor = WiredConditionInputGuard.normalizeTeamColor(data.teamColor, GameTeamColors.RED);
this.userSource = WiredConditionInputGuard.normalizeUserSource(data.userSource);
this.quantifier = this.normalizeQuantifier(data.quantifier, QUANTIFIER_ANY);
} else {
if (!wiredData.equals(""))
this.teamColor = GameTeamColors.values()[Integer.parseInt(wiredData)];
this.teamColor = WiredConditionInputGuard.normalizeTeamColorType(Integer.parseInt(wiredData), GameTeamColors.RED);
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = QUANTIFIER_ANY;
}
@@ -147,8 +147,8 @@ public class WiredConditionTeamMember extends InteractionWiredCondition {
public boolean saveData(WiredSettings settings) {
if(settings.getIntParams().length < 1) return false;
int[] params = settings.getIntParams();
this.teamColor = GameTeamColors.values()[params[0]];
this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER;
this.teamColor = WiredConditionInputGuard.normalizeTeamColorType(params[0], GameTeamColors.RED);
this.userSource = (params.length > 1) ? WiredConditionInputGuard.normalizeUserSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2], QUANTIFIER_ALL) : QUANTIFIER_ANY;
return true;
@@ -42,6 +42,10 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition {
@Override
public boolean evaluate(WiredContext ctx) {
if (ctx == null || ctx.room() == null) {
return false;
}
this.refresh();
List<RoomUnit> userTargets = WiredSourceUtil.resolveUsers(ctx, this.userSource);
@@ -104,39 +108,42 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition {
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.items.clear();
this.onPickUp();
String wiredData = set.getString("wired_data");
if (wiredData == null || wiredData.isEmpty()) {
return;
}
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.furniSource = data.furniSource;
this.userSource = data.userSource;
this.furniSource = WiredFurniConditionInputGuard.normalizeFurniSource(data.furniSource);
this.userSource = WiredFurniConditionInputGuard.normalizeUserSource(data.userSource);
this.quantifier = this.normalizeQuantifier(data.quantifier);
for(int id : data.itemIds) {
for(int id : WiredFurniConditionInputGuard.sanitizeItemIds(data.itemIds, WiredManager.MAXIMUM_FURNI_SELECTION)) {
HabboItem item = room.getHabboItem(id);
if (item != null) {
this.items.add(item);
HabboItem item = room.getHabboItem(id);
if (item != null) {
this.items.add(item);
}
}
}
} else {
String[] data = wiredData.split(";");
for (int id : WiredFurniConditionInputGuard.parseLegacyItemIds(wiredData, WiredManager.MAXIMUM_FURNI_SELECTION)) {
HabboItem item = room.getHabboItem(id);
for (String s : data) {
HabboItem item = room.getHabboItem(Integer.parseInt(s));
if (item != null) {
this.items.add(item);
if (item != null) {
this.items.add(item);
}
} catch (NumberFormatException ignored) {
}
}
this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED;
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = QUANTIFIER_ALL;
}
if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) {
this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
}
this.furniSource = WiredFurniConditionInputGuard.selectedOrNormalizedFurniSource(this.furniSource, !this.items.isEmpty());
}
@Override
@@ -182,13 +189,11 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition {
if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) return false;
int[] params = settings.getIntParams();
this.furniSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER;
this.userSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER;
this.furniSource = (params.length > 0) ? WiredFurniConditionInputGuard.normalizeFurniSource(params[0]) : WiredSourceUtil.SOURCE_TRIGGER;
this.userSource = (params.length > 1) ? WiredFurniConditionInputGuard.normalizeUserSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2]) : QUANTIFIER_ALL;
if (count > 0 && this.furniSource == WiredSourceUtil.SOURCE_TRIGGER) {
this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
}
this.furniSource = WiredFurniConditionInputGuard.selectedOrNormalizedFurniSource(this.furniSource, count > 0);
this.items.clear();
@@ -233,6 +238,22 @@ public class WiredConditionTriggerOnFurni extends InteractionWiredCondition {
return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
}
int normalizeFurniSource(int value) {
switch (value) {
case WiredSourceUtil.SOURCE_SELECTED:
case WiredSourceUtil.SOURCE_SELECTOR:
case WiredSourceUtil.SOURCE_SIGNAL:
case WiredSourceUtil.SOURCE_TRIGGER:
return value;
default:
return WiredSourceUtil.SOURCE_TRIGGER;
}
}
int normalizeUserSource(int value) {
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
}
@Override
public WiredConditionOperator operator() {
return WiredConditionOperator.AND;
@@ -33,6 +33,7 @@ public class WiredConditionTriggererMatch extends InteractionWiredCondition {
protected static final int QUANTIFIER_ALL = 0;
protected static final int QUANTIFIER_ANY = 1;
protected static final int SOURCE_SPECIFIED_USERNAME = 101;
protected static final int MAX_USERNAME_LENGTH = 64;
public static final WiredConditionType type = WiredConditionType.TRIGGERER_MATCH;
@@ -84,7 +85,14 @@ public class WiredConditionTriggererMatch extends InteractionWiredCondition {
return;
}
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
JsonData data;
try {
data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
} catch (RuntimeException exception) {
this.resetSettings();
return;
}
if (data == null) {
return;
}
@@ -284,7 +292,7 @@ public class WiredConditionTriggererMatch extends InteractionWiredCondition {
return "";
}
private int normalizeEntityType(int value) {
int normalizeEntityType(int value) {
switch (value) {
case ENTITY_HABBO:
case ENTITY_PET:
@@ -295,19 +303,19 @@ public class WiredConditionTriggererMatch extends InteractionWiredCondition {
}
}
private int normalizeAvatarMode(int value) {
int normalizeAvatarMode(int value) {
return (value == AVATAR_MODE_CERTAIN) ? AVATAR_MODE_CERTAIN : AVATAR_MODE_ANY;
}
private int normalizeQuantifier(int value) {
int normalizeQuantifier(int value) {
return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
}
private int normalizePrimaryUserSource(int value) {
int normalizePrimaryUserSource(int value) {
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
}
private int normalizeCompareUserSource(int value) {
int normalizeCompareUserSource(int value) {
switch (value) {
case WiredSourceUtil.SOURCE_CLICKED_USER:
case SOURCE_SPECIFIED_USERNAME:
@@ -317,8 +325,13 @@ public class WiredConditionTriggererMatch extends InteractionWiredCondition {
}
}
private String normalizeUsername(String value) {
return (value == null) ? "" : value.trim();
String normalizeUsername(String value) {
if (value == null) {
return "";
}
String normalized = value.trim();
return normalized.length() <= MAX_USERNAME_LENGTH ? normalized : normalized.substring(0, MAX_USERNAME_LENGTH);
}
protected static class MatchResult {
@@ -87,7 +87,13 @@ public class WiredConditionUserPerformsAction extends InteractionWiredCondition
return;
}
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
JsonData data;
try {
data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
} catch (RuntimeException ignored) {
this.resetSettings();
return;
}
if (data == null) {
return;
@@ -253,7 +259,7 @@ public class WiredConditionUserPerformsAction extends InteractionWiredCondition
}
long timestamp = (Long) timestampValue;
if ((System.currentTimeMillis() - timestamp) > TRANSIENT_ACTION_WINDOW_MS) {
if (!WiredUserActionInputGuard.isRecentTimestamp(timestamp, System.currentTimeMillis(), TRANSIENT_ACTION_WINDOW_MS)) {
return false;
}
@@ -35,6 +35,7 @@ public class WiredConditionVariableAgeMatch extends WiredConditionHasVariable {
private static final int DURATION_UNIT_WEEKS = 5;
private static final int DURATION_UNIT_MONTHS = 6;
private static final int DURATION_UNIT_YEARS = 7;
static final int MAX_DURATION_AMOUNT = 1_000_000;
protected int compareValue = COMPARE_VALUE_CREATED;
protected int comparison = COMPARISON_LOWER_THAN;
@@ -97,7 +98,7 @@ public class WiredConditionVariableAgeMatch extends WiredConditionHasVariable {
this.targetType = (params.length > 0) ? normalizeTargetTypeExtended(params[0]) : TARGET_USER;
this.compareValue = (params.length > 1) ? normalizeCompareValue(params[1]) : COMPARE_VALUE_CREATED;
this.comparison = (params.length > 2) ? normalizeComparison(params[2]) : COMPARISON_LOWER_THAN;
this.durationAmount = Math.max(0, (params.length > 3) ? params[3] : 0);
this.durationAmount = normalizeDurationAmount((params.length > 3) ? params[3] : 0);
this.durationUnit = (params.length > 4) ? normalizeDurationUnit(params[4]) : DURATION_UNIT_SECONDS;
this.userSource = (params.length > 5) ? normalizeUserSource(params[5]) : WiredSourceUtil.SOURCE_TRIGGER;
this.furniSource = (params.length > 6) ? normalizeFurniSource(params[6]) : WiredSourceUtil.SOURCE_TRIGGER;
@@ -130,6 +131,10 @@ public class WiredConditionVariableAgeMatch extends WiredConditionHasVariable {
@Override
public boolean evaluate(WiredContext ctx) {
if (ctx == null) {
return false;
}
Room room = ctx.room();
if (room == null || this.variableToken == null || this.variableToken.isEmpty() || !isCustomVariableToken(this.variableToken)) {
@@ -192,7 +197,7 @@ public class WiredConditionVariableAgeMatch extends WiredConditionHasVariable {
this.targetType = normalizeTargetTypeExtended(data.targetType);
this.compareValue = normalizeCompareValue(data.compareValue);
this.comparison = normalizeComparison(data.comparison);
this.durationAmount = Math.max(0, data.durationAmount);
this.durationAmount = normalizeDurationAmount(data.durationAmount);
this.durationUnit = normalizeDurationUnit(data.durationUnit);
this.userSource = normalizeUserSource(data.userSource);
this.furniSource = normalizeFurniSource(data.furniSource);
@@ -345,8 +350,8 @@ public class WiredConditionVariableAgeMatch extends WiredConditionHasVariable {
return Math.max(0L, System.currentTimeMillis() - timestampMs);
}
private static long durationToMillis(int amount, int unit) {
long normalizedAmount = Math.max(0L, amount);
static long durationToMillis(int amount, int unit) {
long normalizedAmount = normalizeDurationAmount(amount);
return switch (unit) {
case DURATION_UNIT_MILLISECONDS -> normalizedAmount;
@@ -367,22 +372,26 @@ public class WiredConditionVariableAgeMatch extends WiredConditionHasVariable {
return left * right;
}
private static int normalizeTargetTypeExtended(int value) {
static int normalizeDurationAmount(int value) {
return Math.max(0, Math.min(MAX_DURATION_AMOUNT, value));
}
static int normalizeTargetTypeExtended(int value) {
return switch (value) {
case TARGET_FURNI, TARGET_CONTEXT, TARGET_ROOM -> value;
default -> TARGET_USER;
};
}
private static int normalizeCompareValue(int value) {
static int normalizeCompareValue(int value) {
return (value == COMPARE_VALUE_UPDATED) ? COMPARE_VALUE_UPDATED : COMPARE_VALUE_CREATED;
}
private static int normalizeComparison(int value) {
static int normalizeComparison(int value) {
return (value == COMPARISON_HIGHER_THAN) ? COMPARISON_HIGHER_THAN : COMPARISON_LOWER_THAN;
}
private static int normalizeDurationUnit(int value) {
static int normalizeDurationUnit(int value) {
return switch (value) {
case DURATION_UNIT_MILLISECONDS, DURATION_UNIT_SECONDS, DURATION_UNIT_MINUTES, DURATION_UNIT_HOURS,
DURATION_UNIT_DAYS, DURATION_UNIT_WEEKS, DURATION_UNIT_MONTHS, DURATION_UNIT_YEARS -> value;
@@ -51,6 +51,7 @@ public class WiredConditionVariableValueMatch extends WiredConditionHasVariable
private static final int COMPARISON_NOT_EQUAL = 5;
private static final String DELIM = "\t";
private static final String FURNI_DELIM = ";";
static final int MAX_ABS_REFERENCE_CONSTANT = 1_000_000_000;
protected int comparison = COMPARISON_EQUAL;
protected int referenceMode = REFERENCE_CONSTANT;
@@ -123,7 +124,7 @@ public class WiredConditionVariableValueMatch extends WiredConditionHasVariable
int nextTargetType = normalizeTargetTypeExtended(param(params, 0, TARGET_USER));
int nextComparison = normalizeComparison(param(params, 1, COMPARISON_EQUAL));
int nextReferenceMode = normalizeReferenceMode(param(params, 2, REFERENCE_CONSTANT));
int nextReferenceConstantValue = param(params, 3, 0);
int nextReferenceConstantValue = normalizeReferenceConstantValue(param(params, 3, 0));
int nextReferenceTargetType = normalizeTargetTypeExtended(param(params, 4, TARGET_USER));
int nextUserSource = normalizeUserSource(param(params, 5, WiredSourceUtil.SOURCE_TRIGGER));
int nextFurniSource = normalizeFurniSource(param(params, 6, WiredSourceUtil.SOURCE_TRIGGER));
@@ -168,6 +169,10 @@ public class WiredConditionVariableValueMatch extends WiredConditionHasVariable
@Override
public boolean evaluate(WiredContext ctx) {
if (ctx == null) {
return false;
}
Room room = ctx.room();
if (room == null || this.variableToken == null || this.variableToken.isEmpty()) {
@@ -220,14 +225,21 @@ public class WiredConditionVariableValueMatch extends WiredConditionHasVariable
String wiredData = set.getString("wired_data");
if (wiredData == null || wiredData.isEmpty() || !wiredData.startsWith("{")) return;
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
JsonData data;
try {
data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
} catch (RuntimeException exception) {
this.onPickUp();
return;
}
if (data == null) return;
this.targetType = normalizeTargetTypeExtended(data.targetType);
this.setVariableToken(normalizeVariableToken((data.variableToken != null) ? data.variableToken : ((data.variableItemId > 0) ? String.valueOf(data.variableItemId) : "")));
this.comparison = normalizeComparison(data.comparison);
this.referenceMode = normalizeReferenceMode(data.referenceMode);
this.referenceConstantValue = data.referenceConstantValue;
this.referenceConstantValue = normalizeReferenceConstantValue(data.referenceConstantValue);
this.referenceTargetType = normalizeTargetTypeExtended(data.referenceTargetType);
this.setReferenceVariableToken(normalizeVariableToken((data.referenceVariableToken != null) ? data.referenceVariableToken : ((data.referenceVariableItemId > 0) ? String.valueOf(data.referenceVariableItemId) : "")));
this.userSource = normalizeUserSource(data.userSource);
@@ -737,32 +749,36 @@ public class WiredConditionVariableValueMatch extends WiredConditionHasVariable
return (params.length > index) ? params[index] : fallback;
}
private static int normalizeTargetTypeExtended(int value) {
static int normalizeTargetTypeExtended(int value) {
return switch (value) {
case TARGET_FURNI, TARGET_CONTEXT, TARGET_ROOM -> value;
default -> TARGET_USER;
};
}
private static int normalizeReferenceMode(int value) {
static int normalizeReferenceMode(int value) {
return (value == REFERENCE_VARIABLE) ? REFERENCE_VARIABLE : REFERENCE_CONSTANT;
}
private static int normalizeReferenceFurniSource(int value) {
static int normalizeReferenceFurniSource(int value) {
return switch (value) {
case SOURCE_SECONDARY_SELECTED, WiredSourceUtil.SOURCE_SELECTOR, WiredSourceUtil.SOURCE_SIGNAL -> value;
default -> WiredSourceUtil.SOURCE_TRIGGER;
};
}
private static int normalizeComparison(int value) {
static int normalizeComparison(int value) {
return switch (value) {
case COMPARISON_GREATER_THAN, COMPARISON_GREATER_THAN_OR_EQUAL, COMPARISON_LESS_THAN_OR_EQUAL, COMPARISON_LESS_THAN, COMPARISON_NOT_EQUAL -> value;
default -> COMPARISON_EQUAL;
};
}
private static int parseInteger(String value) {
static int normalizeReferenceConstantValue(int value) {
return Math.max(-MAX_ABS_REFERENCE_CONSTANT, Math.min(MAX_ABS_REFERENCE_CONSTANT, value));
}
static int parseInteger(String value) {
try {
return (value == null || value.trim().isEmpty()) ? 0 : Integer.parseInt(value.trim());
} catch (NumberFormatException e) {
@@ -0,0 +1,21 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
public final class WiredDateRangeInputGuard {
private WiredDateRangeInputGuard() {
}
public static int[] normalizeRange(int startDate, int endDate) {
int start = normalizeTimestamp(startDate);
int end = normalizeTimestamp(endDate);
if (start > end) {
return new int[]{0, 0};
}
return new int[]{start, end};
}
public static int normalizeTimestamp(int value) {
return Math.max(0, value);
}
}
@@ -0,0 +1,76 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public final class WiredFurniConditionInputGuard {
private WiredFurniConditionInputGuard() {
}
public static int normalizeFurniSource(int value) {
switch (value) {
case WiredSourceUtil.SOURCE_SELECTED:
case WiredSourceUtil.SOURCE_SELECTOR:
case WiredSourceUtil.SOURCE_SIGNAL:
case WiredSourceUtil.SOURCE_TRIGGER:
return value;
default:
return WiredSourceUtil.SOURCE_TRIGGER;
}
}
public static int normalizeUserSource(int value) {
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
}
public static int selectedOrNormalizedFurniSource(int value, boolean hasSelectedItems) {
int source = normalizeFurniSource(value);
return (hasSelectedItems && source == WiredSourceUtil.SOURCE_TRIGGER)
? WiredSourceUtil.SOURCE_SELECTED
: source;
}
public static List<Integer> sanitizeItemIds(Collection<Integer> itemIds, int maxCount) {
List<Integer> result = new ArrayList<>();
if (itemIds == null || maxCount < 1) {
return result;
}
for (Integer itemId : itemIds) {
if (itemId == null || itemId < 1 || result.size() >= maxCount) {
continue;
}
result.add(itemId);
}
return result;
}
public static List<Integer> parseLegacyItemIds(String value, int maxCount) {
List<Integer> result = new ArrayList<>();
if (value == null || value.isBlank() || maxCount < 1) {
return result;
}
for (String part : value.split("[;,:\\t]")) {
if (result.size() >= maxCount) {
break;
}
try {
int id = Integer.parseInt(part.trim());
if (id > 0) {
result.add(id);
}
} catch (NumberFormatException ignored) {
// Ignore malformed legacy item ids.
}
}
return result;
}
}
@@ -0,0 +1,92 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredMatchFurniSetting;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public final class WiredMatchPositionInputGuard {
public static final int MAX_STATE_LENGTH = 512;
private WiredMatchPositionInputGuard() {
}
public static int normalizeFurniSource(int value, boolean hasSelectedSettings) {
int source = switch (value) {
case WiredSourceUtil.SOURCE_SELECTED, WiredSourceUtil.SOURCE_SELECTOR,
WiredSourceUtil.SOURCE_SIGNAL, WiredSourceUtil.SOURCE_TRIGGER -> value;
default -> WiredSourceUtil.SOURCE_TRIGGER;
};
return (hasSelectedSettings && source == WiredSourceUtil.SOURCE_TRIGGER)
? WiredSourceUtil.SOURCE_SELECTED
: source;
}
public static List<WiredMatchFurniSetting> sanitizeSettings(Collection<WiredMatchFurniSetting> settings, Room room) {
List<WiredMatchFurniSetting> result = new ArrayList<>();
if (settings == null || room == null) {
return result;
}
for (WiredMatchFurniSetting setting : settings) {
WiredMatchFurniSetting normalized = sanitizeSetting(setting, room);
if (normalized != null) {
result.add(normalized);
}
if (result.size() >= WiredManager.MAXIMUM_FURNI_SELECTION) {
break;
}
}
return result;
}
public static WiredMatchFurniSetting sanitizeSetting(WiredMatchFurniSetting setting, Room room) {
if (setting == null || room == null) {
return null;
}
return sanitizeParts(setting.item_id, setting.state, setting.rotation, setting.x, setting.y, setting.z, room);
}
public static WiredMatchFurniSetting sanitizeParts(int itemId, String state, int rotation, int x, int y, double z, Room room) {
if (itemId < 1 || room == null) {
return null;
}
HabboItem item = room.getHabboItem(itemId);
if (item == null || rotation < 0 || rotation > 7 || !Double.isFinite(z)) {
return null;
}
if (x < Short.MIN_VALUE || x > Short.MAX_VALUE || y < Short.MIN_VALUE || y > Short.MAX_VALUE) {
return null;
}
if (room.getLayout() != null && room.getLayout().getTile((short) x, (short) y) == null) {
return null;
}
return new WiredMatchFurniSetting(itemId, normalizeState(state), rotation, x, y, z);
}
public static String normalizeState(String state) {
if (state == null) {
return "";
}
String normalized = state.replace('\t', ' ').replace('\r', ' ').replace('\n', ' ');
if (normalized.length() > MAX_STATE_LENGTH) {
return normalized.substring(0, MAX_STATE_LENGTH);
}
return normalized;
}
}
@@ -0,0 +1,14 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
public final class WiredUserActionInputGuard {
private WiredUserActionInputGuard() {
}
public static boolean isRecentTimestamp(long timestamp, long now, long windowMs) {
if (timestamp < 1 || timestamp > now || windowMs < 1) {
return false;
}
return (now - timestamp) <= windowMs;
}
}
@@ -0,0 +1,45 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
public final class WiredUserConditionInputGuard {
public static final int MAX_BADGE_CODE_LENGTH = 64;
public static final int MAX_EFFECT_ID = 10_000;
public static final int MAX_HAND_ITEM_ID = 10_000;
private WiredUserConditionInputGuard() {
}
public static String normalizeBadgeCode(String value) {
if (value == null) {
return "";
}
String normalized = value.trim().replace('\t', ' ').replace('\r', ' ').replace('\n', ' ');
if (normalized.length() > MAX_BADGE_CODE_LENGTH) {
return normalized.substring(0, MAX_BADGE_CODE_LENGTH);
}
return normalized;
}
public static int normalizeUserSource(int value) {
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
}
public static int normalizeEffectId(int value) {
if (value < 0) {
return 0;
}
return Math.min(value, MAX_EFFECT_ID);
}
public static int normalizeHandItemId(int value) {
if (value < 0) {
return 0;
}
return Math.min(value, MAX_HAND_ITEM_ID);
}
}
@@ -5,6 +5,7 @@ import com.eu.habbo.habbohotel.gameclients.GameClient;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredNumericInputGuard;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -72,11 +73,11 @@ public class WiredEffectGiveHotelviewBonusRarePoints extends InteractionWiredEff
@Override
public boolean saveData(WiredSettings settings, GameClient gameClient) {
try {
this.amount = Integer.parseInt(settings.getStringParam());
} catch (Exception e) {
int nextAmount = WiredNumericInputGuard.parsePositiveAmount(settings.getStringParam(), WiredNumericInputGuard.maxRewardAmount());
if (nextAmount <= 0) {
return false;
}
this.amount = nextAmount;
int[] params = settings.getIntParams();
this.userSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER;
@@ -5,6 +5,7 @@ import com.eu.habbo.habbohotel.gameclients.GameClient;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredNumericInputGuard;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -71,11 +72,11 @@ public class WiredEffectGiveHotelviewHofPoints extends InteractionWiredEffect {
@Override
public boolean saveData(WiredSettings settings, GameClient gameClient) {
try {
this.amount = Integer.parseInt(settings.getStringParam());
} catch (Exception e) {
int nextAmount = WiredNumericInputGuard.parsePositiveAmount(settings.getStringParam(), WiredNumericInputGuard.maxRewardAmount());
if (nextAmount <= 0) {
return false;
}
this.amount = nextAmount;
int[] params = settings.getIntParams();
this.userSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER;
@@ -6,6 +6,7 @@ import com.eu.habbo.habbohotel.gameclients.GameClient;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredNumericInputGuard;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -72,11 +73,11 @@ public class WiredEffectGiveRespect extends InteractionWiredEffect {
@Override
public boolean saveData(WiredSettings settings, GameClient gameClient) {
try {
this.respects = Integer.parseInt(settings.getStringParam());
} catch (Exception e) {
int nextRespects = WiredNumericInputGuard.parsePositiveAmount(settings.getStringParam(), WiredNumericInputGuard.maxRespectAmount());
if (nextRespects <= 0) {
return false;
}
this.respects = nextRespects;
int[] params = settings.getIntParams();
this.userSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER;
@@ -5,6 +5,7 @@ import com.eu.habbo.habbohotel.gameclients.GameClient;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredLegacyDataGuard;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.pets.RideablePet;
import com.eu.habbo.habbohotel.rooms.*;
@@ -287,16 +288,11 @@ public class WiredEffectTeleport extends InteractionWiredEffect {
String[] wiredDataOld = wiredData.split("\t");
if (wiredDataOld.length >= 1) {
this.setDelay(Integer.parseInt(wiredDataOld[0]));
this.setDelay(WiredLegacyDataGuard.parseDelay(wiredDataOld[0]));
}
if (wiredDataOld.length == 2) {
if (wiredDataOld[1].contains(";")) {
for (String s : wiredDataOld[1].split(";")) {
HabboItem item = room.getHabboItem(Integer.parseInt(s));
if (item != null)
this.items.add(item);
}
this.items.addAll(WiredLegacyDataGuard.parseRoomItems(wiredDataOld[1], room));
}
}
this.fastTeleport = false;
@@ -15,6 +15,7 @@ import com.eu.habbo.habbohotel.items.interactions.games.freeze.InteractionFreeze
import com.eu.habbo.habbohotel.items.interactions.games.tag.InteractionTagField;
import com.eu.habbo.habbohotel.items.interactions.games.tag.InteractionTagPole;
import com.eu.habbo.habbohotel.items.interactions.pets.*;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredLegacyDataGuard;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -274,18 +275,15 @@ public class WiredEffectToggleFurni extends InteractionWiredEffect {
String[] wiredDataOld = wiredData.split("\t");
if (wiredDataOld.length >= 1) {
this.setDelay(Integer.parseInt(wiredDataOld[0]));
this.setDelay(WiredLegacyDataGuard.parseDelay(wiredDataOld[0]));
}
if (wiredDataOld.length == 2) {
if (wiredDataOld[1].contains(";")) {
for (String s : wiredDataOld[1].split(";")) {
HabboItem item = room.getHabboItem(Integer.parseInt(s));
for (HabboItem item : WiredLegacyDataGuard.parseRoomItems(wiredDataOld[1], room)) {
if (item instanceof InteractionFreezeBlock || item instanceof InteractionFreezeTile || item instanceof InteractionCrackable)
continue;
if (item != null)
this.items.add(item);
this.items.add(item);
}
}
}
@@ -4,6 +4,7 @@ import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTimerInputGuard;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTriggerReset;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -29,6 +30,9 @@ import java.util.List;
*/
public class WiredTriggerAtSetTime extends InteractionWiredTrigger implements WiredTickable, WiredTriggerReset {
public static final WiredTriggerType type = WiredTriggerType.AT_GIVEN_TIME;
private static final int STEP_MS = 500;
private static final int MIN_DELAY = STEP_MS;
private static final int LEGACY_FALLBACK_DELAY = 20 * STEP_MS;
/** The time in milliseconds until the trigger fires */
public int executeTime;
@@ -68,18 +72,19 @@ public class WiredTriggerAtSetTime extends InteractionWiredTrigger implements Wi
public void loadWiredData(ResultSet set, Room room) throws SQLException {
String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.executeTime = data.executeTime;
} else {
if (wiredData.length() >= 1) {
this.executeTime = (Integer.parseInt(wiredData));
Integer storedExecuteTime = null;
try {
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
storedExecuteTime = data != null ? data.executeTime : null;
} else if (wiredData != null && wiredData.length() >= 1) {
storedExecuteTime = Integer.parseInt(wiredData);
}
} catch (RuntimeException ignored) {
storedExecuteTime = null;
}
if (this.executeTime < 500) {
this.executeTime = 20 * 500;
}
this.executeTime = WiredTimerInputGuard.normalizeStoredMillis(storedExecuteTime, MIN_DELAY, LEGACY_FALLBACK_DELAY);
// Initialize for tick system - will be registered by RoomItemManager
this.accumulatedTime = 0;
@@ -134,7 +139,7 @@ public class WiredTriggerAtSetTime extends InteractionWiredTrigger implements Wi
@Override
public boolean saveData(WiredSettings settings) {
if (settings.getIntParams().length < 1) return false;
this.executeTime = settings.getIntParams()[0] * 500;
this.executeTime = WiredTimerInputGuard.fromClientUnits(settings.getIntParams()[0], STEP_MS, MIN_DELAY);
this.resetTimer();
@@ -4,6 +4,7 @@ import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTimerInputGuard;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTriggerReset;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -29,6 +30,9 @@ import java.util.List;
*/
public class WiredTriggerAtTimeLong extends InteractionWiredTrigger implements WiredTickable, WiredTriggerReset {
private static final WiredTriggerType type = WiredTriggerType.AT_GIVEN_TIME;
private static final int STEP_MS = 500;
private static final int MIN_DELAY = STEP_MS;
private static final int LEGACY_FALLBACK_DELAY = 20 * STEP_MS;
/** The time in milliseconds until the trigger fires */
private int executeTime;
@@ -68,18 +72,19 @@ public class WiredTriggerAtTimeLong extends InteractionWiredTrigger implements W
public void loadWiredData(ResultSet set, Room room) throws SQLException {
String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.executeTime = data.executeTime;
} else {
if (wiredData.length() >= 1) {
this.executeTime = (Integer.parseInt(wiredData));
Integer storedExecuteTime = null;
try {
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
storedExecuteTime = data != null ? data.executeTime : null;
} else if (wiredData != null && wiredData.length() >= 1) {
storedExecuteTime = Integer.parseInt(wiredData);
}
} catch (RuntimeException ignored) {
storedExecuteTime = null;
}
if (this.executeTime < 500) {
this.executeTime = 20 * 500;
}
this.executeTime = WiredTimerInputGuard.normalizeStoredMillis(storedExecuteTime, MIN_DELAY, LEGACY_FALLBACK_DELAY);
// Initialize for tick system
this.accumulatedTime = 0;
@@ -134,7 +139,7 @@ public class WiredTriggerAtTimeLong extends InteractionWiredTrigger implements W
@Override
public boolean saveData(WiredSettings settings) {
if (settings.getIntParams().length < 1) return false;
this.executeTime = settings.getIntParams()[0] * 500;
this.executeTime = WiredTimerInputGuard.fromClientUnits(settings.getIntParams()[0], STEP_MS, MIN_DELAY);
this.resetTimer();
@@ -4,6 +4,7 @@ import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTimerInputGuard;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTriggerReset;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -30,6 +31,9 @@ import java.util.List;
public class WiredTriggerRepeater extends InteractionWiredTrigger implements WiredTickable, WiredTriggerReset {
public static final WiredTriggerType type = WiredTriggerType.PERIODICALLY;
public static final int DEFAULT_DELAY = 10 * 500; // 5 seconds default
private static final int STEP_MS = 500;
private static final int MIN_DELAY = STEP_MS;
private static final int LEGACY_FALLBACK_DELAY = 20 * STEP_MS;
/** The interval in milliseconds between triggers */
protected int repeatTime = DEFAULT_DELAY;
@@ -63,18 +67,19 @@ public class WiredTriggerRepeater extends InteractionWiredTrigger implements Wir
public void loadWiredData(ResultSet set, Room room) throws SQLException {
String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.repeatTime = data.repeatTime;
} else {
if (wiredData.length() >= 1) {
this.repeatTime = (Integer.parseInt(wiredData));
Integer storedRepeatTime = null;
try {
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
storedRepeatTime = data != null ? data.repeatTime : null;
} else if (wiredData != null && wiredData.length() >= 1) {
storedRepeatTime = Integer.parseInt(wiredData);
}
} catch (RuntimeException ignored) {
storedRepeatTime = null;
}
if (this.repeatTime < 500) {
this.repeatTime = 20 * 500;
}
this.repeatTime = WiredTimerInputGuard.normalizeStoredMillis(storedRepeatTime, MIN_DELAY, LEGACY_FALLBACK_DELAY);
}
@Override
@@ -123,13 +128,7 @@ public class WiredTriggerRepeater extends InteractionWiredTrigger implements Wir
@Override
public boolean saveData(WiredSettings settings) {
if (settings.getIntParams().length < 1) return false;
int newRepeatTime = settings.getIntParams()[0] * 500;
if (newRepeatTime < 500) {
newRepeatTime = 500;
}
this.repeatTime = newRepeatTime;
this.repeatTime = WiredTimerInputGuard.fromClientUnits(settings.getIntParams()[0], STEP_MS, MIN_DELAY);
return true;
}
@@ -4,6 +4,7 @@ import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTimerInputGuard;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTriggerReset;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -29,6 +30,9 @@ import java.util.List;
*/
public class WiredTriggerRepeaterLong extends InteractionWiredTrigger implements WiredTickable, WiredTriggerReset {
public static final int DEFAULT_DELAY = 10 * 5000; // 50 seconds default
private static final int STEP_MS = 5000;
private static final int MIN_DELAY = STEP_MS;
private static final int LEGACY_FALLBACK_DELAY = 20 * STEP_MS;
private static final WiredTriggerType type = WiredTriggerType.PERIODICALLY_LONG;
/** The interval in milliseconds between triggers */
@@ -63,18 +67,19 @@ public class WiredTriggerRepeaterLong extends InteractionWiredTrigger implements
public void loadWiredData(ResultSet set, Room room) throws SQLException {
String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.repeatTime = data.repeatTime;
} else {
if (wiredData.length() >= 1) {
this.repeatTime = (Integer.parseInt(wiredData));
Integer storedRepeatTime = null;
try {
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
storedRepeatTime = data != null ? data.repeatTime : null;
} else if (wiredData != null && wiredData.length() >= 1) {
storedRepeatTime = Integer.parseInt(wiredData);
}
} catch (RuntimeException ignored) {
storedRepeatTime = null;
}
if (this.repeatTime < 5000) {
this.repeatTime = 20 * 5000;
}
this.repeatTime = WiredTimerInputGuard.normalizeStoredMillis(storedRepeatTime, MIN_DELAY, LEGACY_FALLBACK_DELAY);
}
@Override
@@ -123,11 +128,7 @@ public class WiredTriggerRepeaterLong extends InteractionWiredTrigger implements
@Override
public boolean saveData(WiredSettings settings) {
if (settings.getIntParams().length < 1) return false;
int interval = settings.getIntParams()[0];
if (interval < 1) {
interval = 1;
}
this.repeatTime = interval * 5000;
this.repeatTime = WiredTimerInputGuard.fromClientUnits(settings.getIntParams()[0], STEP_MS, MIN_DELAY);
// No accumulated time reset needed - using global tick count
return true;
}
@@ -3,6 +3,7 @@ package com.eu.habbo.habbohotel.items.interactions.wired.triggers;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTimerInputGuard;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.wired.WiredTriggerType;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
@@ -94,8 +95,7 @@ public class WiredTriggerRepeaterShort extends WiredTriggerRepeater {
public boolean saveData(WiredSettings settings) {
if (settings.getIntParams().length < 1) return false;
int newRepeatTime = settings.getIntParams()[0] * STEP_MS;
this.repeatTime = clampRepeatTime(newRepeatTime);
this.repeatTime = WiredTimerInputGuard.fromClientUnits(settings.getIntParams()[0], STEP_MS, MIN_DELAY, MAX_DELAY);
return true;
}
@@ -11,6 +11,10 @@ public enum PermissionSetting {
ROOM_OWNER;
public static PermissionSetting fromString(String value) {
if (value == null) {
return DISALLOWED;
}
switch (value) {
case "1":
return ALLOWED;
@@ -11,6 +11,7 @@ import org.slf4j.LoggerFactory;
import java.sql.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -65,35 +66,45 @@ public class PermissionsManager {
}
private void loadPermissionsLegacy(Connection connection) throws SQLException {
Set<Integer> loadedRankIds = new HashSet<>();
try (Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT * FROM permissions ORDER BY id ASC")) {
while (set.next()) {
int rankId = set.getInt("id");
loadedRankIds.add(rankId);
Rank rank = null;
if (!this.ranks.containsKey(set.getInt("id"))) {
if (!this.ranks.containsKey(rankId)) {
rank = new Rank(set);
this.ranks.put(set.getInt("id"), rank);
this.ranks.put(rankId, rank);
} else {
rank = this.ranks.get(set.getInt("id"));
rank = this.ranks.get(rankId);
rank.load(set);
}
this.addBadgeMapping(rank);
}
}
this.pruneMissingRanks(loadedRankIds);
}
private boolean loadPermissionsNormalized(Connection connection) throws SQLException {
boolean hasRanks = false;
List<Rank> loadedRanks = new ArrayList<>();
Set<Integer> loadedRankIds = new HashSet<>();
try (Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT * FROM permission_ranks ORDER BY id ASC")) {
while (set.next()) {
hasRanks = true;
int rankId = set.getInt("id");
loadedRankIds.add(rankId);
Rank rank = this.ranks.get(set.getInt("id"));
Rank rank = this.ranks.get(rankId);
if (rank == null) {
rank = new Rank(set.getInt("id"));
this.ranks.put(set.getInt("id"), rank);
rank = new Rank(rankId);
this.ranks.put(rankId, rank);
}
rank.loadNormalizedMetadata(set);
@@ -141,9 +152,18 @@ public class PermissionsManager {
}
}
this.pruneMissingRanks(loadedRankIds);
return hasDefinitions;
}
private void pruneMissingRanks(Set<Integer> loadedRankIds) {
for (int rankId : this.ranks.keys()) {
if (!loadedRankIds.contains(rankId)) {
this.ranks.remove(rankId);
}
}
}
private void ensureNormalizedRankColumns(Connection connection, List<Rank> loadedRanks) throws SQLException {
Set<String> availableColumns = new HashSet<>();
@@ -254,6 +274,10 @@ public class PermissionsManager {
public boolean hasPermission(Habbo habbo, String permission, boolean withRoomRights) {
if (habbo == null || habbo.getHabboInfo() == null || permission == null || permission.isBlank()) {
return false;
}
if (!this.hasPermission(habbo.getHabboInfo().getRank(), permission, withRoomRights)) {
for (HabboPlugin plugin : Emulator.getPluginManager().getPlugins()) {
if (plugin.hasPermission(habbo, permission)) {
@@ -269,15 +293,16 @@ public class PermissionsManager {
public boolean hasPermission(Rank rank, String permission, boolean withRoomRights) {
return rank.hasPermission(permission, withRoomRights);
return rank != null && permission != null && !permission.isBlank() && rank.hasPermission(permission, withRoomRights);
}
public Set<String> getStaffBadges() {
return this.badges.keySet();
return Collections.unmodifiableSet(new HashSet<>(this.badges.keySet()));
}
public List<Rank> getRanksByBadgeCode(String code) {
return this.badges.get(code);
List<Rank> ranks = this.badges.get(code);
return ranks == null ? Collections.emptyList() : Collections.unmodifiableList(new ArrayList<>(ranks));
}
public List<Rank> getAllRanks() {
@@ -114,6 +114,10 @@ public class Rank {
}
public boolean hasPermission(String key, boolean isRoomOwner) {
if (key == null || key.isBlank()) {
return false;
}
if (this.permissions.containsKey(key)) {
Permission permission = this.permissions.get(key);
@@ -23,6 +23,7 @@ public class RoomTrade {
//Configuration. Loaded from database & updated accordingly.
public static boolean TRADING_ENABLED = true;
public static boolean TRADING_REQUIRES_PERK = true;
public static final int MAX_OFFERED_ITEMS = 100;
private final List<RoomTradeUser> users;
private final Room room;
@@ -58,7 +59,7 @@ public class RoomTrade {
public synchronized void offerItem(Habbo habbo, HabboItem item) {
RoomTradeUser user = this.getRoomTradeUserForHabbo(habbo);
if (user == null || item == null || user.getItems().contains(item))
if (user == null || item == null || user.getItems().contains(item) || user.getItems().size() >= MAX_OFFERED_ITEMS)
return;
habbo.getInventory().getItemsComponent().removeHabboItem(item);
@@ -75,6 +76,9 @@ public class RoomTrade {
return;
for (HabboItem item : items) {
if (user.getItems().size() >= MAX_OFFERED_ITEMS)
break;
if (!user.getItems().contains(item)) {
habbo.getInventory().getItemsComponent().removeHabboItem(item);
user.getItems().add(item);
@@ -12,10 +12,15 @@ public class CatalogSearchedItemEvent extends MessageHandler {
public void handle() throws Exception {
int offerId = this.packet.readInt();
int pageId = Emulator.getGameEnvironment().getCatalogManager().offerDefs.get(offerId);
int catalogItemId = Emulator.getGameEnvironment().getCatalogManager().offerDefs.get(offerId);
if (pageId != 0) {
CatalogPage page = Emulator.getGameEnvironment().getCatalogManager().getCatalogPage(Emulator.getGameEnvironment().getCatalogManager().getCatalogItem(pageId).getPageId());
if (catalogItemId != 0) {
CatalogItem requestedItem = Emulator.getGameEnvironment().getCatalogManager().getCatalogItem(catalogItemId);
if (requestedItem == null) {
return;
}
CatalogPage page = Emulator.getGameEnvironment().getCatalogManager().getCatalogPage(requestedItem.getPageId());
if (page != null) {
TIntObjectIterator<CatalogItem> iterator = page.getCatalogItems().iterator();
@@ -25,7 +30,7 @@ public class CatalogSearchedItemEvent extends MessageHandler {
CatalogItem item = iterator.value();
if (item.getOfferId() == offerId) {
if (item.getSearchOfferId() == offerId) {
this.client.sendResponse(new CatalogSearchResultComposer(item));
return;
}
@@ -13,6 +13,10 @@ public class BuyItemEvent extends MessageHandler {
public void handle() throws Exception {
int offerId = this.packet.readInt();
if (!MarketplaceInputGuard.isPositiveId(offerId)) {
return;
}
MarketPlace.buyItem(offerId, this.client);
}
}
@@ -0,0 +1,47 @@
package com.eu.habbo.messages.incoming.catalog.marketplace;
import com.eu.habbo.habbohotel.catalog.marketplace.MarketPlace;
final class MarketplaceInputGuard {
static final int MAX_SEARCH_LENGTH = 30;
static final int DEFAULT_SORT = 1;
static final int MIN_SORT = 1;
static final int MAX_SORT = 6;
private MarketplaceInputGuard() {
}
static boolean isPositiveId(int id) {
return id > 0;
}
static String normalizeSearch(String query) {
if (query == null) {
return "";
}
String normalized = query.trim();
return normalized.length() > MAX_SEARCH_LENGTH ? normalized.substring(0, MAX_SEARCH_LENGTH) : normalized;
}
static int normalizeSort(int sort) {
return sort >= MIN_SORT && sort <= MAX_SORT ? sort : DEFAULT_SORT;
}
static int normalizeMinPrice(int minPrice) {
if (minPrice == -1) {
return -1;
}
return Math.max(0, Math.min(minPrice, MarketPlace.MAXIMUM_LISTING_PRICE));
}
static int normalizeMaxPrice(int maxPrice, int minPrice) {
if (maxPrice == -1) {
return -1;
}
int normalized = Math.max(0, Math.min(maxPrice, MarketPlace.MAXIMUM_LISTING_PRICE));
return minPrice > 0 && normalized > 0 && normalized < minPrice ? minPrice : normalized;
}
}
@@ -9,6 +9,10 @@ public class RequestItemInfoEvent extends MessageHandler {
this.packet.readInt();
int id = this.packet.readInt();
if (!MarketplaceInputGuard.isPositiveId(id)) {
return;
}
this.client.sendResponse(new MarketplaceItemInfoComposer(id));
}
}
@@ -20,14 +20,10 @@ public class RequestOffersEvent extends MessageHandler {
@Override
public void handle() throws Exception {
int min = this.packet.readInt();
int max = this.packet.readInt();
String query = this.packet.readString();
int type = this.packet.readInt();
if (query.length() > 30) {
query = query.substring(0, 30);
}
int min = MarketplaceInputGuard.normalizeMinPrice(this.packet.readInt());
int max = MarketplaceInputGuard.normalizeMaxPrice(this.packet.readInt(), min);
String query = MarketplaceInputGuard.normalizeSearch(this.packet.readString());
int type = MarketplaceInputGuard.normalizeSort(this.packet.readInt());
boolean tryCache = min == -1 && max == -1 && query.isEmpty();
@@ -29,6 +29,7 @@ public class SellItemEvent extends MessageHandler {
final int furniType = this.packet.readInt(); // 1 = FLOOR_TYPE, 2 = WALL_TYPE
final int itemId = this.packet.readInt();
if (!MarketplaceInputGuard.isPositiveId(itemId)) return;
if (furniType != 1 && furniType != 2) return;
HabboItem item = this.client.getHabbo().getInventory().getItemsComponent().getHabboItem(itemId);
@@ -12,6 +12,11 @@ public class TakeBackItemEvent extends MessageHandler {
@Override
public void handle() throws Exception {
int offerId = this.packet.readInt();
if (!MarketplaceInputGuard.isPositiveId(offerId)) {
return;
}
MarketPlace.takeBackItem(this.client.getHabbo(), offerId);
}
}
@@ -25,14 +25,16 @@ public class AcceptFriendRequestEvent extends MessageHandler {
@Override
public void handle() throws Exception {
int count = Math.min(this.packet.readInt(), 100);
int count = this.packet.readInt();
if (count <= 0 || count > 100) return;
int userId;
for (int i = 0; i < count; i++) {
userId = this.packet.readInt();
if (userId == 0)
return;
if (userId <= 0)
continue;
if (this.client.getHabbo().getMessenger().getFriends().containsKey(userId)) {
this.client.getHabbo().getMessenger().deleteFriendRequests(userId, this.client.getHabbo().getHabboInfo().getId());
@@ -1,5 +1,6 @@
package com.eu.habbo.messages.incoming.friends;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.messenger.MessengerBuddy;
import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.friends.UpdateFriendComposer;
@@ -12,12 +13,16 @@ public class ChangeRelationEvent extends MessageHandler {
int relationId = this.packet.readInt();
MessengerBuddy buddy = this.client.getHabbo().getMessenger().getFriends().get(userId);
if (buddy != null && relationId >= 0 && relationId <= 3) {
if (buddy != null && FriendInputGuard.isValidRelation(relationId)) {
UserRelationShipEvent event = new UserRelationShipEvent(this.client.getHabbo(), buddy, relationId);
if (!event.isCancelled()) {
buddy.setRelation(event.relationShip);
this.client.sendResponse(new UpdateFriendComposer(this.client.getHabbo(), buddy, 0));
}
if (Emulator.getPluginManager().fireEvent(event).isCancelled())
return;
if (!FriendInputGuard.isValidRelation(event.relationShip))
return;
buddy.setRelation(event.relationShip);
this.client.sendResponse(new UpdateFriendComposer(this.client.getHabbo(), buddy, 0));
}
}
}
@@ -3,6 +3,8 @@ package com.eu.habbo.messages.incoming.friends;
import com.eu.habbo.messages.incoming.MessageHandler;
public class DeclineFriendRequestEvent extends MessageHandler {
private static final int MAX_BATCH_SIZE = 100;
@Override
public void handle() throws Exception {
boolean all = this.packet.readBoolean();
@@ -11,10 +13,11 @@ public class DeclineFriendRequestEvent extends MessageHandler {
this.client.getHabbo().getMessenger().deleteAllFriendRequests(this.client.getHabbo().getHabboInfo().getId());
} else {
int count = this.packet.readInt();
if (count <= 0 || count > MAX_BATCH_SIZE) return;
for (int i = 0; i < count; i++) {
this.client.getHabbo().getMessenger().deleteFriendRequests(this.packet.readInt(), this.client.getHabbo().getHabboInfo().getId());
}
}
}
}
}
@@ -0,0 +1,31 @@
package com.eu.habbo.messages.incoming.friends;
final class FriendInputGuard {
static final int MAX_USERNAME_LENGTH = 15;
static final int MAX_MESSAGE_LENGTH = 255;
static final int MAX_RELATION_ID = 3;
private FriendInputGuard() {
}
static String normalizeUsername(String username) {
return username == null ? "" : username.trim();
}
static boolean isValidUsername(String username) {
return username != null && !username.isBlank() && username.length() <= MAX_USERNAME_LENGTH;
}
static String normalizeMessage(String message) {
if (message == null) {
return "";
}
String normalized = message.trim();
return normalized.length() > MAX_MESSAGE_LENGTH ? normalized.substring(0, MAX_MESSAGE_LENGTH) : normalized;
}
static boolean isValidRelation(int relationId) {
return relationId >= 0 && relationId <= MAX_RELATION_ID;
}
}
@@ -9,7 +9,11 @@ public class FriendPrivateMessageEvent extends MessageHandler {
@Override
public void handle() throws Exception {
int userId = this.packet.readInt();
String message = this.packet.readString();
String message = FriendInputGuard.normalizeMessage(this.packet.readString());
if (message.isEmpty()) {
return;
}
if (!this.client.getHabbo().getHabboStats().allowTalk()) {
return;
@@ -25,8 +29,6 @@ public class FriendPrivateMessageEvent extends MessageHandler {
if (buddy == null)
return;
if (message.length() > 255) message = message.substring(0, 255);
UserFriendChatEvent event = new UserFriendChatEvent(this.client.getHabbo(), buddy, message);
if (Emulator.getPluginManager().fireEvent(event).isCancelled())
return;
@@ -26,9 +26,9 @@ public class FriendRequestEvent extends MessageHandler {
@Override
public void handle() throws Exception {
String username = this.packet.readString();
String username = FriendInputGuard.normalizeUsername(this.packet.readString());
if (this.client == null || username == null || username.isEmpty())
if (this.client == null || !FriendInputGuard.isValidUsername(username))
return;
// TargetHabbo can be null if the Habbo is not online or when the Habbo doesn't exist
@@ -62,6 +62,12 @@ public class FriendRequestEvent extends MessageHandler {
if (targetId == this.client.getHabbo().getHabboInfo().getId())
return;
if (this.client.getHabbo().getMessenger().getFriends().containsKey(targetId))
return;
if (Messenger.friendRequested(targetId, this.client.getHabbo().getHabboInfo().getId()) || Messenger.friendRequested(this.client.getHabbo().getHabboInfo().getId(), targetId))
return;
// Target Habbo exists
// Check if Habbo is accepting friend requests
if (targetBlocksFriendRequests) {
@@ -23,9 +23,14 @@ public class InviteFriendsEvent extends MessageHandler {
userIds[i] = this.packet.readInt();
}
String message = this.packet.readString();
String message = FriendInputGuard.normalizeMessage(this.packet.readString());
if (message.isEmpty()) {
return;
}
message = Emulator.getGameEnvironment().getWordFilter().filter(message, this.client.getHabbo());
message = FriendInputGuard.normalizeMessage(message);
for (int i : userIds) {
if (i == 0)
@@ -8,6 +8,7 @@ import com.eu.habbo.messages.outgoing.friends.RemoveFriendComposer;
import gnu.trove.list.array.TIntArrayList;
public class RemoveFriendEvent extends MessageHandler {
private static final int MAX_BATCH_SIZE = 100;
private final TIntArrayList removedFriends;
@@ -18,8 +19,12 @@ public class RemoveFriendEvent extends MessageHandler {
@Override
public void handle() throws Exception {
int count = this.packet.readInt();
if (count <= 0 || count > MAX_BATCH_SIZE) return;
for (int i = 0; i < count; i++) {
int habboId = this.packet.readInt();
if (habboId <= 0) continue;
this.removedFriends.add(habboId);
Messenger.unfriend(this.client.getHabbo().getHabboInfo().getId(), habboId);
@@ -21,12 +21,21 @@ public class GuildChangeColorsEvent extends MessageHandler {
if (guild != null) {
if (guild.getOwnerId() == this.client.getHabbo().getHabboInfo().getId() || this.client.getHabbo().hasPermission(Permission.ACC_GUILD_ADMIN)) {
GuildChangedColorsEvent colorsEvent = new GuildChangedColorsEvent(guild, this.packet.readInt(), this.packet.readInt());
int colorOne = this.packet.readInt();
int colorTwo = this.packet.readInt();
if (!Emulator.getGameEnvironment().getGuildManager().symbolColor(colorOne) || !Emulator.getGameEnvironment().getGuildManager().backgroundColor(colorTwo))
return;
GuildChangedColorsEvent colorsEvent = new GuildChangedColorsEvent(guild, colorOne, colorTwo);
Emulator.getPluginManager().fireEvent(colorsEvent);
if (colorsEvent.isCancelled())
return;
if (!Emulator.getGameEnvironment().getGuildManager().symbolColor(colorsEvent.colorOne) || !Emulator.getGameEnvironment().getGuildManager().backgroundColor(colorsEvent.colorTwo))
return;
if (guild.getColorOne() != colorsEvent.colorOne || guild.getColorTwo() != colorsEvent.colorTwo) {
guild.setColorOne(colorsEvent.colorOne);
guild.setColorTwo(colorsEvent.colorTwo);
@@ -23,6 +23,10 @@ public class GuildChangeNameDescEvent extends MessageHandler {
if (guild.getOwnerId() == this.client.getHabbo().getHabboInfo().getId() || this.client.getHabbo().hasPermission(Permission.ACC_GUILD_ADMIN)) {
String newName = Emulator.getGameEnvironment().getWordFilter().filter(this.packet.readString(), this.client.getHabbo());
String newDesc = Emulator.getGameEnvironment().getWordFilter().filter(this.packet.readString(), this.client.getHabbo());
if (!GuildInputLimits.isValidGuildName(newName) || !GuildInputLimits.isValidGuildDescription(newDesc))
return;
GuildChangedNameEvent nameEvent = new GuildChangedNameEvent(guild, newName, newDesc);
Emulator.getPluginManager().fireEvent(nameEvent);
@@ -32,7 +36,7 @@ public class GuildChangeNameDescEvent extends MessageHandler {
if (guild.getName().equals(nameEvent.name) && guild.getDescription().equals(nameEvent.description))
return;
if(nameEvent.name.length() > 29 || nameEvent.description.length() > 254)
if (!GuildInputLimits.isValidGuildName(nameEvent.name) || !GuildInputLimits.isValidGuildDescription(nameEvent.description))
return;
guild.setName(nameEvent.name);
@@ -40,12 +40,20 @@ public class GuildChangeSettingsEvent extends MessageHandler {
if (guild != null) {
if (guild.getOwnerId() == this.client.getHabbo().getHabboInfo().getId() || this.client.getHabbo().hasPermission(Permission.ACC_GUILD_ADMIN)) {
GuildChangedSettingsEvent settingsEvent = new GuildChangedSettingsEvent(guild, this.packet.readInt(), this.packet.readInt() == 0);
int state = this.packet.readInt();
if (state < 0 || state >= GuildState.values().length)
return;
GuildChangedSettingsEvent settingsEvent = new GuildChangedSettingsEvent(guild, state, this.packet.readInt() == 0);
Emulator.getPluginManager().fireEvent(settingsEvent);
if (settingsEvent.isCancelled())
return;
if (settingsEvent.state < 0 || settingsEvent.state >= GuildState.values().length)
return;
guild.setState(GuildState.valueOf(settingsEvent.state));
guild.setRights(settingsEvent.rights);
@@ -0,0 +1,17 @@
package com.eu.habbo.messages.incoming.guilds;
final class GuildInputLimits {
static final int MAX_GUILD_NAME_LENGTH = 29;
static final int MAX_GUILD_DESCRIPTION_LENGTH = 254;
private GuildInputLimits() {
}
static boolean isValidGuildName(String name) {
return name != null && !name.isBlank() && name.length() <= MAX_GUILD_NAME_LENGTH;
}
static boolean isValidGuildDescription(String description) {
return description != null && description.length() <= MAX_GUILD_DESCRIPTION_LENGTH;
}
}
@@ -31,11 +31,11 @@ public class RequestGuildBuyEvent extends MessageHandler {
final String name = Emulator.getGameEnvironment().getWordFilter().filter(this.packet.readString(), this.client.getHabbo());
final String description = Emulator.getGameEnvironment().getWordFilter().filter(this.packet.readString(), this.client.getHabbo());
if (name.length() == 0 || name.length() > 29) {
if (!GuildInputLimits.isValidGuildName(name)) {
this.client.sendResponse(new GuildEditFailComposer(GuildEditFailComposer.INVALID_GUILD_NAME));
return;
}
if (description.length() > 254) {
if (!GuildInputLimits.isValidGuildDescription(description)) {
return;
}
@@ -68,6 +68,11 @@ public class RequestGuildBuyEvent extends MessageHandler {
int colorOne = this.packet.readInt();
int colorTwo = this.packet.readInt();
if (!Emulator.getGameEnvironment().getGuildManager().symbolColor(colorOne) || !Emulator.getGameEnvironment().getGuildManager().backgroundColor(colorTwo)) {
this.client.sendResponse(new GuildEditFailComposer(GuildEditFailComposer.INVALID_GUILD_NAME));
return;
}
int count = this.packet.readInt();
String badge = GuildBadgeBuilder.readBadge(this.packet, count);
@@ -9,6 +9,10 @@ import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.guilds.GuildMembersComposer;
public class RequestGuildMembersEvent extends MessageHandler {
private static final int MAX_PAGE_ID = 1000;
private static final int MAX_QUERY_LENGTH = 32;
private static final int MAX_LEVEL_ID = 2;
@Override
public int getRatelimit() {
return 500;
@@ -20,6 +24,9 @@ public class RequestGuildMembersEvent extends MessageHandler {
int pageId = this.packet.readInt();
String query = this.packet.readString();
int levelId = this.packet.readInt();
if (pageId < 0 || pageId > MAX_PAGE_ID || levelId < 0 || levelId > MAX_LEVEL_ID || query == null || query.length() > MAX_QUERY_LENGTH) {
return;
}
Guild g = Emulator.getGameEnvironment().getGuildManager().getGuild(groupId);
@@ -8,6 +8,7 @@ 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.handshake.ConnectionErrorComposer;
public class GuildForumDataEvent extends MessageHandler {
@Override
@@ -19,6 +20,11 @@ public class GuildForumDataEvent extends MessageHandler {
public void handle() throws Exception {
int guildId = packet.readInt();
if (!GuildForumInputGuard.isPositiveId(guildId)) {
this.client.sendResponse(new ConnectionErrorComposer(400));
return;
}
Guild guild = Emulator.getGameEnvironment().getGuildManager().getGuild(guildId);
if (guild == null) return;
@@ -2,6 +2,7 @@ package com.eu.habbo.messages.incoming.guilds.forums;
final class GuildForumInputGuard {
static final int MAX_PAGE_LIMIT = 50;
static final int MAX_THREAD_INDEX = 1000;
static final int MAX_MARK_READ_BATCH = 50;
private GuildForumInputGuard() {
@@ -19,6 +20,10 @@ final class GuildForumInputGuard {
return index >= 0 && limit > 0 && limit <= MAX_PAGE_LIMIT;
}
static boolean isValidThreadIndex(int index) {
return index >= 0 && index <= MAX_THREAD_INDEX;
}
static boolean isValidMarkReadBatch(int count) {
return count > 0 && count <= MAX_MARK_READ_BATCH;
}
@@ -1,6 +1,9 @@
package com.eu.habbo.messages.incoming.guilds.forums;
import com.eu.habbo.Emulator;
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.outgoing.guilds.forums.GuildForumDataComposer;
import org.slf4j.Logger;
@@ -37,6 +40,17 @@ public class GuildForumMarkAsReadEvent extends MessageHandler {
continue;
}
Guild guild = Emulator.getGameEnvironment().getGuildManager().getGuild(guildId);
if (guild == null || !guild.hasForum()) {
continue;
}
GuildMember member = Emulator.getGameEnvironment().getGuildManager().getGuildMember(guildId, userId);
boolean staff = this.client.getHabbo().hasPermission(Permission.ACC_MODTOOL_TICKET_Q);
if (!guild.canHabboReadForum(userId, member, staff)) {
continue;
}
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement(
"INSERT INTO `guild_forum_views` (`user_id`, `guild_id`, `timestamp`) VALUES (?, ?, ?) " +
"ON DUPLICATE KEY UPDATE `timestamp` = ?"
@@ -22,7 +22,7 @@ public class GuildForumThreadsEvent extends MessageHandler {
int guildId = packet.readInt();
int index = packet.readInt();
if (!GuildForumInputGuard.isPositiveId(guildId) || index < 0) {
if (!GuildForumInputGuard.isPositiveId(guildId) || !GuildForumInputGuard.isValidThreadIndex(index)) {
this.client.sendResponse(new ConnectionErrorComposer(400));
return;
}
@@ -50,6 +50,7 @@ import java.util.Date;
@NoAuthMessage
public class SecureLoginEvent extends MessageHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(SecureLoginEvent.class);
private static final int MAX_SSO_TICKET_LENGTH = 128;
@Override
public int getRatelimit() {
@@ -72,17 +73,17 @@ public class SecureLoginEvent extends MessageHandler {
return;
}
String sso = this.packet.readString().replace(" ", "");
String sso = SecureLoginInputGuard.normalizeSsoTicket(this.packet.readString());
if (Emulator.getPluginManager().fireEvent(new SSOAuthenticationEvent(sso)).isCancelled()) {
if (!SecureLoginInputGuard.isValidSsoTicket(sso)) {
Emulator.getGameServer().getGameClientManager().disposeClient(this.client);
LOGGER.info("SSO Authentication is cancelled by a plugin. Closed connection...");
LOGGER.debug("Client is trying to connect with an invalid SSO ticket! Closed connection...");
return;
}
if (sso.isEmpty()) {
if (sso.isEmpty() || sso.length() > MAX_SSO_TICKET_LENGTH) {
Emulator.getGameServer().getGameClientManager().disposeClient(this.client);
LOGGER.debug("Client is trying to connect without SSO ticket! Closed connection...");
LOGGER.debug("Client is trying to connect with missing or invalid SSO ticket! Closed connection...");
return;
}
@@ -0,0 +1,20 @@
package com.eu.habbo.messages.incoming.handshake;
final class SecureLoginInputGuard {
static final int MAX_SSO_TICKET_LENGTH = 512;
private SecureLoginInputGuard() {
}
static String normalizeSsoTicket(String ticket) {
if (ticket == null) {
return "";
}
return ticket.replace(" ", "");
}
static boolean isValidSsoTicket(String ticket) {
return ticket != null && !ticket.isEmpty() && ticket.length() <= MAX_SSO_TICKET_LENGTH;
}
}
@@ -14,7 +14,7 @@ public class ModToolAlertEvent extends MessageHandler {
int userId = this.packet.readInt();
String message = ModToolInputGuard.normalize(this.packet.readString());
if (!ModToolInputGuard.isSafeMessage(message)) {
if (!ModToolTicketGuard.isPositiveId(userId) || !ModToolInputGuard.isSafeMessage(message)) {
return;
}
@@ -10,7 +10,13 @@ public class ModToolChangeRoomSettingsEvent extends MessageHandler {
@Override
public void handle() throws Exception {
if (this.client.getHabbo().hasPermission(Permission.ACC_SUPPORTTOOL)) {
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.packet.readInt());
int roomId = this.packet.readInt();
if (!ModToolTicketGuard.isPositiveId(roomId)) {
return;
}
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(roomId);
if (room != null) {
final boolean lockDoor = this.packet.readInt() == 1;

Some files were not shown because too many files have changed in this diff Show More