Merge branch 'main' into dev

This commit is contained in:
DuckieTM
2026-03-21 09:06:27 +01:00
87 changed files with 6575 additions and 521 deletions
@@ -0,0 +1,22 @@
SET NAMES utf8mb4;
-- Create the hotel timezone setting if it does not exist yet.
INSERT INTO `emulator_settings` (`key`, `value`)
SELECT 'hotel.timezone', 'Europe/Rome'
WHERE NOT EXISTS (
SELECT 1
FROM `emulator_settings`
WHERE `key` = 'hotel.timezone'
);
-- Keep the default/example value aligned for existing installs too.
UPDATE `emulator_settings`
SET `value` = 'Europe/Rome'
WHERE `key` = 'hotel.timezone';
-- Helper query for a timezone selector.
-- If MySQL/MariaDB timezone tables are populated, this returns the available timezone ids.
SELECT `Name` AS `timezone_id`
FROM `mysql`.`time_zone_name`
WHERE `Name` IS NOT NULL
ORDER BY `Name`;
@@ -7,6 +7,7 @@ import com.eu.habbo.core.*;
import com.eu.habbo.core.consolecommands.ConsoleCommand;
import com.eu.habbo.database.Database;
import com.eu.habbo.habbohotel.GameEnvironment;
import com.eu.habbo.habbohotel.gameclients.SessionResumeManager;
import com.eu.habbo.networking.gameserver.GameServer;
import com.eu.habbo.networking.rconserver.RCONServer;
import com.eu.habbo.plugin.PluginManager;
@@ -20,6 +21,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.net.JarURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.sql.Timestamp;
@@ -52,6 +55,7 @@ public final class Emulator {
"Still Rocking in 2026.\n";
public static String build = "";
public static long buildTimestamp = -1L;
public static boolean isReady = false;
public static boolean isShuttingDown = false;
public static boolean stopped = false;
@@ -103,13 +107,6 @@ public final class Emulator {
System.out.println(logo);
System.out.println();
LOGGER.info("https://github.com/duckietm/Arcturus-Morningstar-Extended, ");
System.out.println();
LOGGER.info("This project is for educational purposes only. This Emulator is an open-source fork of Arcturus created by TheGeneral.");
LOGGER.info("Version: {}", version);
LOGGER.info("Build: {}", build);
long startTime = System.nanoTime();
Emulator.runtime = Runtime.getRuntime();
@@ -141,6 +138,15 @@ public final class Emulator {
Emulator.config.register("camera.price.points", "0");
Emulator.config.register("camera.price.points.type", "5");
Emulator.config.register("camera.render.delay", "5");
Emulator.config.register("hotel.timezone", java.time.ZoneId.systemDefault().getId());
String hotelTimezoneId = Emulator.getConfig().getValue("hotel.timezone", java.time.ZoneId.systemDefault().getId());
System.out.println();
LOGGER.info("https://github.com/duckietm/Arcturus-Morningstar-Extended, ");
System.out.println();
LOGGER.info("This project is for educational purposes only. This Emulator is an open-source fork of Arcturus created by TheGeneral.");
LOGGER.info("Version: {}", version);
LOGGER.info("Build: {}", build);
LOGGER.info("Build Timestamp: {} [{}]", formatBuildTimestamp(buildTimestamp, hotelTimezoneId), hotelTimezoneId);
Emulator.texts.register("camera.permission", "You don't have permission to use the camera!");
Emulator.texts.register("camera.wait", "Please wait %seconds% seconds before making another picture.");
Emulator.texts.register("camera.error.creation", "Failed to create your picture. *sadpanda*");
@@ -216,12 +222,21 @@ public final class Emulator {
private static void setBuild() {
if (Emulator.class.getProtectionDomain().getCodeSource() == null) {
build = "UNKNOWN";
buildTimestamp = -1L;
return;
}
StringBuilder sb = new StringBuilder();
try {
String filepath = new File(Emulator.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getAbsolutePath();
File buildFile = new File(Emulator.class.getProtectionDomain().getCodeSource().getLocation().toURI());
buildTimestamp = resolveBuildTimestamp(buildFile);
if (!buildFile.isFile()) {
build = "DEV";
return;
}
String filepath = buildFile.getAbsolutePath();
MessageDigest md = MessageDigest.getInstance("MD5");
try (FileInputStream fis = new FileInputStream(filepath)) {
byte[] dataBytes = new byte[1024];
@@ -234,14 +249,69 @@ public final class Emulator {
}
} catch (Exception e) {
build = "UNKNOWN";
buildTimestamp = -1L;
return;
}
build = sb.toString();
}
private static long resolveBuildTimestamp(File buildFile) {
if (buildFile != null && buildFile.exists() && buildFile.isFile()) {
return buildFile.lastModified();
}
try {
URL classUrl = Emulator.class.getResource("Emulator.class");
if (classUrl != null) {
if ("file".equalsIgnoreCase(classUrl.getProtocol())) {
File classFile = new File(classUrl.toURI());
if (classFile.exists()) {
return classFile.lastModified();
}
}
if ("jar".equalsIgnoreCase(classUrl.getProtocol())) {
JarURLConnection connection = (JarURLConnection) classUrl.openConnection();
File jarFile = new File(connection.getJarFileURL().toURI());
if (jarFile.exists()) {
return jarFile.lastModified();
}
}
}
} catch (Exception ignored) {
}
if (buildFile != null && buildFile.exists()) {
return buildFile.lastModified();
}
return -1L;
}
private static String formatBuildTimestamp(long buildTimestamp, String timezoneId) {
if (buildTimestamp <= 0) {
return "UNKNOWN";
}
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
format.setTimeZone(TimeZone.getTimeZone(java.time.ZoneId.of(timezoneId)));
} catch (Exception ignored) {
format.setTimeZone(TimeZone.getDefault());
}
return format.format(new Timestamp(buildTimestamp));
}
private static void dispose() {
Emulator.getThreading().setCanAdd(false);
if (Emulator.threading != null) {
Emulator.threading.setCanAdd(false);
}
Emulator.isShuttingDown = true;
Emulator.isReady = false;
@@ -250,6 +320,7 @@ public final class Emulator {
if (Emulator.pluginManager != null)
tryShutdown(() -> Emulator.pluginManager.fireEvent(new EmulatorStartShutdownEvent()));
if (Emulator.rconServer != null) tryShutdown(() -> Emulator.rconServer.stop());
tryShutdown(() -> SessionResumeManager.getInstance().disposeAll());
if (Emulator.gameEnvironment != null) tryShutdown(() -> Emulator.gameEnvironment.dispose());
if (Emulator.pluginManager != null)
tryShutdown(() -> Emulator.pluginManager.fireEvent(new EmulatorStoppedEvent()));
@@ -87,6 +87,7 @@ public class ConfigurationManager {
// Runtime
envMapping.put("runtime.threads", "RT_THREADS");
envMapping.put("logging.errors.runtime", "RT_LOG_ERRORS");
envMapping.put("hotel.timezone", "HOTEL_TIMEZONE");
for (Map.Entry<String, String> entry : envMapping.entrySet()) {
String envValue = System.getenv(entry.getValue());
@@ -301,7 +301,7 @@ public class CommandHandler {
addCommand(new ListPrefixesCommand());
addCommand(new RemovePrefixCommand());
addCommand(new PrefixBlacklistCommand());
addCommand(new WiredCommand());
addCommand(new TestCommand());
}
@@ -29,7 +29,7 @@ public class UnmuteCommand extends Command {
}
if (habbo.getHabboInfo().getCurrentRoom() != null && habbo.getHabboInfo().getCurrentRoom().isMuted(habbo)) {
habbo.getHabboInfo().getCurrentRoom().muteHabbo(habbo, 1);
habbo.getHabboInfo().getCurrentRoom().unmuteHabbo(habbo);
}
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.succes.cmd_unmute").replace("%user%", params[1]), RoomChatMessageBubbles.ALERT);
@@ -0,0 +1,35 @@
package com.eu.habbo.habbohotel.commands;
import com.eu.habbo.habbohotel.gameclients.GameClient;
import com.eu.habbo.habbohotel.permissions.Permission;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomChatMessageBubbles;
import com.eu.habbo.messages.outgoing.users.InClientLinkComposer;
public class WiredCommand extends Command {
public WiredCommand() {
super(Permission.ACC_PLACEFURNI, new String[]{"wired"});
}
@Override
public boolean handle(GameClient gameClient, String[] params) throws Exception {
Room room = gameClient.getHabbo().getHabboInfo().getCurrentRoom();
if (room == null) {
gameClient.getHabbo().whisper("You need to be inside a room to use :wired.", RoomChatMessageBubbles.ALERT);
return true;
}
boolean hasRights = room.hasRights(gameClient.getHabbo())
|| room.isOwner(gameClient.getHabbo())
|| gameClient.getHabbo().hasPermission(Permission.ACC_ANYROOMOWNER);
if (!hasRights) {
gameClient.getHabbo().whisper("You need room rights to open the Wired Creator Tools.", RoomChatMessageBubbles.ALERT);
return true;
}
gameClient.sendResponse(new InClientLinkComposer("wired-tools/show"));
return true;
}
}
@@ -26,6 +26,7 @@ public class GameClient {
private Habbo habbo;
private boolean handshakeFinished;
private String machineId = "";
private String ssoTicket = "";
public final ConcurrentHashMap<Integer, Integer> incomingPacketCounter = new ConcurrentHashMap<>(25);
public final ConcurrentHashMap<Class<? extends MessageHandler>, Long> messageTimestamps = new ConcurrentHashMap<>();
@@ -82,6 +83,14 @@ public class GameClient {
this.machineId = machineId;
}
public String getSsoTicket() {
return this.ssoTicket;
}
public void setSsoTicket(String ssoTicket) {
this.ssoTicket = ssoTicket != null ? ssoTicket : "";
}
public void sendResponse(MessageComposer composer) {
this.sendResponse(composer.compose());
}
@@ -145,9 +154,16 @@ public class GameClient {
if (this.habbo != null) {
if (this.habbo.isOnline()) {
// Try to park the habbo in the grace period instead of immediate disconnect
boolean parked = SessionResumeManager.getInstance().parkHabbo(this.habbo, this.ssoTicket);
if (!parked) {
// No grace period configured — immediate disconnect as before
this.habbo.getHabboInfo().setOnline(false);
this.habbo.disconnect();
}
// If parked, do NOT call disconnect() — the habbo stays in the room
}
this.habbo = null;
}
@@ -116,6 +116,22 @@ public class GameClientManager {
}
/**
* Find an existing GameClient that authenticated with the given SSO ticket.
* Used to detect reconnections where the old connection hasn't been closed yet.
*/
public GameClient findClientBySsoTicket(String ssoTicket) {
if (ssoTicket == null || ssoTicket.isEmpty()) return null;
for (GameClient client : this.clients.values()) {
if (ssoTicket.equals(client.getSsoTicket()) && client.getHabbo() != null) {
return client;
}
}
return null;
}
public List<Habbo> getHabbosWithMachineId(String machineId) {
List<Habbo> habbos = new ArrayList<>();
@@ -0,0 +1,173 @@
package com.eu.habbo.habbohotel.gameclients;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.users.Habbo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
/**
* Manages a grace period for disconnected users. Instead of immediately
* disposing a Habbo when their WebSocket drops, the Habbo is held in
* a "ghost" state for a configurable number of seconds. If the same
* user reconnects (via SSO ticket) within the grace window, their
* existing Habbo object is resumed on the new connection — keeping
* them in their room, preserving inventory state, etc.
*
* Config key: session.reconnect.grace.seconds (default: 30)
*/
public class SessionResumeManager {
private static final Logger LOGGER = LoggerFactory.getLogger(SessionResumeManager.class);
private static SessionResumeManager instance;
private final ConcurrentHashMap<Integer, GhostSession> ghostSessions = new ConcurrentHashMap<>();
public static SessionResumeManager getInstance() {
if (instance == null) {
instance = new SessionResumeManager();
}
return instance;
}
public int getGracePeriodSeconds() {
return Emulator.getConfig().getInt("session.reconnect.grace.seconds", 30);
}
/**
* Park a disconnected Habbo in ghost mode. Their room presence is
* preserved, but the old GameClient channel is closed.
*
* @return true if the habbo was parked (grace period > 0), false if immediate dispose should happen
*/
public boolean parkHabbo(Habbo habbo, String ssoTicket) {
int graceSeconds = getGracePeriodSeconds();
if (graceSeconds <= 0) {
return false;
}
int userId = habbo.getHabboInfo().getId();
// Cancel any existing ghost session for this user
GhostSession existing = ghostSessions.remove(userId);
if (existing != null && existing.disposeFuture != null) {
existing.disposeFuture.cancel(false);
}
LOGGER.info("[SessionResume] Parking {} (id={}) for {}s grace period",
habbo.getHabboInfo().getUsername(), userId, graceSeconds);
// Restore the SSO ticket so the client can reconnect with the same ticket
if (ssoTicket != null && !ssoTicket.isEmpty()) {
restoreSsoTicket(userId, ssoTicket);
}
// Schedule the final disconnect after the grace period
ScheduledFuture<?> future = Emulator.getThreading().run(() -> {
GhostSession ghost = ghostSessions.remove(userId);
if (ghost != null) {
LOGGER.info("[SessionResume] Grace period expired for {} (id={}) - performing full disconnect",
ghost.habbo.getHabboInfo().getUsername(), userId);
performFullDisconnect(ghost.habbo);
}
}, graceSeconds * 1000);
ghostSessions.put(userId, new GhostSession(habbo, ssoTicket, future));
return true;
}
/**
* Try to resume a ghost session for the given user ID.
*
* @return the parked Habbo if found within grace period, null otherwise
*/
public Habbo resumeSession(int userId) {
GhostSession ghost = ghostSessions.remove(userId);
if (ghost == null) {
return null;
}
// Cancel the scheduled dispose
if (ghost.disposeFuture != null) {
ghost.disposeFuture.cancel(false);
}
LOGGER.info("[SessionResume] Resuming session for {} (id={})",
ghost.habbo.getHabboInfo().getUsername(), userId);
return ghost.habbo;
}
/**
* Check if a user has a ghost session (is in grace period).
*/
public boolean hasGhostSession(int userId) {
return ghostSessions.containsKey(userId);
}
/**
* Immediately expire all ghost sessions (e.g. on emulator shutdown).
*/
public void disposeAll() {
for (GhostSession ghost : ghostSessions.values()) {
if (ghost.disposeFuture != null) {
ghost.disposeFuture.cancel(false);
}
performFullDisconnect(ghost.habbo);
}
ghostSessions.clear();
}
/**
* Perform the actual full disconnect that normally happens in Habbo.disconnect().
*/
private void performFullDisconnect(Habbo habbo) {
try {
habbo.getHabboInfo().setOnline(false);
habbo.disconnect();
} catch (Exception e) {
LOGGER.error("[SessionResume] Error during deferred disconnect", e);
}
// Clear the SSO ticket now that the grace period is truly over
clearSsoTicket(habbo.getHabboInfo().getId());
}
private void restoreSsoTicket(int userId, String ssoTicket) {
try (var connection = Emulator.getDatabase().getDataSource().getConnection();
var statement = connection.prepareStatement("UPDATE users SET auth_ticket = ? WHERE id = ? LIMIT 1")) {
statement.setString(1, ssoTicket);
statement.setInt(2, userId);
statement.execute();
LOGGER.info("[SessionResume] Restored SSO ticket for user {} during grace period", userId);
} catch (Exception e) {
LOGGER.error("[SessionResume] Failed to restore SSO ticket for user " + userId, e);
}
}
private void clearSsoTicket(int userId) {
try (var connection = Emulator.getDatabase().getDataSource().getConnection();
var statement = connection.prepareStatement("UPDATE users SET auth_ticket = ? WHERE id = ? LIMIT 1")) {
statement.setString(1, "");
statement.setInt(2, userId);
statement.execute();
} catch (Exception e) {
LOGGER.error("[SessionResume] Failed to clear SSO ticket for user " + userId, e);
}
}
private static class GhostSession {
final Habbo habbo;
final String ssoTicket;
final ScheduledFuture<?> disposeFuture;
GhostSession(Habbo habbo, String ssoTicket, ScheduledFuture<?> disposeFuture) {
this.habbo = habbo;
this.ssoTicket = ssoTicket;
this.disposeFuture = disposeFuture;
}
}
}
@@ -48,15 +48,11 @@ import com.eu.habbo.habbohotel.items.interactions.totems.InteractionTotemLegs;
import com.eu.habbo.habbohotel.items.interactions.totems.InteractionTotemPlanet;
import com.eu.habbo.habbohotel.items.interactions.wired.conditions.*;
import com.eu.habbo.habbohotel.items.interactions.wired.effects.*;
import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectFurniArea;
import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectUsersArea;
import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectUsersNeighborhood;
import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectFurniNeighborhood;
import com.eu.habbo.habbohotel.items.interactions.wired.selector.WiredEffectFurniByType;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredBlob;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraOrEval;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraRandom;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraUnseen;
import com.eu.habbo.habbohotel.items.interactions.wired.selector.*;
import com.eu.habbo.habbohotel.items.interactions.wired.triggers.*;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboItem;
@@ -202,16 +198,25 @@ public class ItemManager {
this.interactionsList.add(new ItemInteraction("random_state", InteractionRandomState.class));
this.interactionsList.add(new ItemInteraction("vendingmachine_no_sides", InteractionNoSidesVendingMachine.class));
this.interactionsList.add(new ItemInteraction("tile_walkmagic", InteractionTileWalkMagic.class));
this.interactionsList.add(new ItemInteraction("antenna", InteractionDefault.class));
this.interactionsList.add(new ItemInteraction("room_invisible_click_tile", InteractionDefault.class));
this.interactionsList.add(new ItemInteraction("game_timer", InteractionGameTimer.class));
this.interactionsList.add(new ItemInteraction("wf_trg_walks_on_furni", WiredTriggerHabboWalkOnFurni.class));
this.interactionsList.add(new ItemInteraction("wf_trg_walks_off_furni", WiredTriggerHabboWalkOffFurni.class));
this.interactionsList.add(new ItemInteraction("wf_trg_click_furni", WiredTriggerHabboClicksFurni.class));
this.interactionsList.add(new ItemInteraction("wf_trg_click_tile", WiredTriggerHabboClicksTile.class));
this.interactionsList.add(new ItemInteraction("wf_trg_click_user", WiredTriggerHabboClicksUser.class));
this.interactionsList.add(new ItemInteraction("wf_trg_user_performs_action", WiredTriggerHabboPerformsAction.class));
this.interactionsList.add(new ItemInteraction("wf_trg_enter_room", WiredTriggerHabboEntersRoom.class));
this.interactionsList.add(new ItemInteraction("wf_trg_leave_room", WiredTriggerHabboLeavesRoom.class));
this.interactionsList.add(new ItemInteraction("wf_trg_says_something", WiredTriggerHabboSaysKeyword.class));
this.interactionsList.add(new ItemInteraction("wf_trg_periodically", WiredTriggerRepeater.class));
this.interactionsList.add(new ItemInteraction("wf_trg_period_short", WiredTriggerRepeaterShort.class));
this.interactionsList.add(new ItemInteraction("wf_trg_period_long", WiredTriggerRepeaterLong.class));
this.interactionsList.add(new ItemInteraction("wf_trg_state_changed", WiredTriggerFurniStateToggled.class));
this.interactionsList.add(new ItemInteraction("wf_trg_stuff_state", WiredTriggerFurniStateToggled.class));
this.interactionsList.add(new ItemInteraction("wf_trg_at_given_time", WiredTriggerAtSetTime.class));
this.interactionsList.add(new ItemInteraction("wf_trg_at_time_long", WiredTriggerAtTimeLong.class));
this.interactionsList.add(new ItemInteraction("wf_trg_collision", WiredTriggerCollision.class));
@@ -255,6 +260,13 @@ public class ItemManager {
this.interactionsList.add(new ItemInteraction("wf_act_alert", WiredEffectAlert.class));
this.interactionsList.add(new ItemInteraction("wf_act_give_handitem", WiredEffectGiveHandItem.class));
this.interactionsList.add(new ItemInteraction("wf_act_give_effect", WiredEffectGiveEffect.class));
this.interactionsList.add(new ItemInteraction("wf_act_freeze", WiredEffectFreeze.class));
this.interactionsList.add(new ItemInteraction("wf_act_unfreeze", WiredEffectUnfreeze.class));
this.interactionsList.add(new ItemInteraction("wf_act_furni_to_user", WiredEffectFurniToUser.class));
this.interactionsList.add(new ItemInteraction("wf_act_user_to_furni", WiredEffectUserToFurni.class));
this.interactionsList.add(new ItemInteraction("wf_act_furni_to_furni", WiredEffectFurniToFurni.class));
this.interactionsList.add(new ItemInteraction("wf_act_set_altitude", WiredEffectSetAltitude.class));
this.interactionsList.add(new ItemInteraction("wf_act_rel_mov", WiredEffectRelativeMove.class));
this.interactionsList.add(new ItemInteraction("wf_slc_furni_area", WiredEffectFurniArea.class));
this.interactionsList.add(new ItemInteraction("wf_slc_furni_neighborhood", WiredEffectFurniNeighborhood.class));
this.interactionsList.add(new ItemInteraction("wf_slc_furni_bytype", WiredEffectFurniByType.class));
@@ -285,8 +297,19 @@ public class ItemManager {
this.interactionsList.add(new ItemInteraction("wf_cnd_actor_in_team", WiredConditionTeamMember.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_trggrer_on_frn", WiredConditionTriggerOnFurni.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_has_handitem", WiredConditionHabboHasHandItem.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_not_has_handitem", WiredConditionNotHabboHasHandItem.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_date_rng_active", WiredConditionDateRangeActive.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_valid_moves", WiredConditionMovementValidation.class));
// this.interactionsList.add(new ItemInteraction("wf_cnd_counter_time_matches", WiredConditionCounterTimeMatches.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_match_time", WiredConditionMatchTime.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_match_date", WiredConditionMatchDate.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_user_performs_action", WiredConditionUserPerformsAction.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_not_user_performs_action", WiredConditionNotUserPerformsAction.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_has_altitude", WiredConditionHasAltitude.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_triggerer_match", WiredConditionTriggererMatch.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_not_triggerer_match", WiredConditionNotTriggererMatch.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_team_has_score", WiredConditionTeamHasScore.class));
this.interactionsList.add(new ItemInteraction("wf_cnd_team_has_rank", WiredConditionTeamHasRank.class));
this.interactionsList.add(new ItemInteraction("wf_xtra_random", WiredExtraRandom.class));
@@ -115,6 +115,14 @@ public class WiredConditionHabboHasHandItem extends InteractionWiredCondition {
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
protected int getHandItem() {
return this.handItem;
}
protected int getUserSource() {
return this.userSource;
}
static class JsonData {
int handItemId;
int userSource;
@@ -0,0 +1,285 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition;
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.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredConditionType;
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;
import gnu.trove.set.hash.THashSet;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.stream.Collectors;
public class WiredConditionHasAltitude extends InteractionWiredCondition {
private static final int COMPARISON_LESS = 0;
private static final int COMPARISON_EQUAL = 1;
private static final int COMPARISON_GREATER = 2;
private static final int QUANTIFIER_ALL = 0;
private static final int QUANTIFIER_ANY = 1;
public static final WiredConditionType type = WiredConditionType.HAS_ALTITUDE;
private final THashSet<HabboItem> items;
private int comparison = COMPARISON_EQUAL;
private double altitude = 0.0D;
private int furniSource = WiredSourceUtil.SOURCE_TRIGGER;
private int quantifier = QUANTIFIER_ALL;
public WiredConditionHasAltitude(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
this.items = new THashSet<>();
}
public WiredConditionHasAltitude(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
this.items = new THashSet<>();
}
@Override
public boolean evaluate(WiredContext ctx) {
Room room = ctx.room();
if (room == null) {
return false;
}
this.refresh(room);
List<HabboItem> targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items);
if (targets.isEmpty()) {
return false;
}
if (this.quantifier == QUANTIFIER_ANY) {
return targets.stream().anyMatch(this::matchesAltitude);
}
return targets.stream().allMatch(this::matchesAltitude);
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.comparison,
this.formatAltitude(this.altitude),
this.furniSource,
this.quantifier,
this.items.stream().map(HabboItem::getId).collect(Collectors.toList())
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.items.clear();
this.comparison = COMPARISON_EQUAL;
this.altitude = 0.0D;
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = QUANTIFIER_ALL;
String wiredData = set.getString("wired_data");
if (wiredData == null || !wiredData.startsWith("{")) {
return;
}
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
if (data == null) {
return;
}
this.comparison = this.normalizeComparison(data.comparison);
this.altitude = this.parseAltitudeOrDefault(data.altitude);
this.furniSource = this.normalizeFurniSource(data.furniSource);
this.quantifier = this.normalizeQuantifier(data.quantifier);
if (data.itemIds == null) {
return;
}
for (Integer id : data.itemIds) {
HabboItem item = room.getHabboItem(id);
if (item != null) {
this.items.add(item);
}
}
}
@Override
public void onPickUp() {
this.items.clear();
this.comparison = COMPARISON_EQUAL;
this.altitude = 0.0D;
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = QUANTIFIER_ALL;
}
@Override
public WiredConditionType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
this.refresh(room);
message.appendBoolean(false);
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
message.appendInt(this.items.size());
for (HabboItem item : this.items) {
message.appendInt(item.getId());
}
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString(this.formatAltitude(this.altitude));
message.appendInt(3);
message.appendInt(this.comparison);
message.appendInt(this.furniSource);
message.appendInt(this.quantifier);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(0);
message.appendInt(0);
}
@Override
public boolean saveData(WiredSettings settings) {
int[] params = settings.getIntParams();
this.comparison = (params.length > 0) ? this.normalizeComparison(params[0]) : COMPARISON_EQUAL;
this.furniSource = (params.length > 1) ? this.normalizeFurniSource(params[1]) : WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = (params.length > 2) ? this.normalizeQuantifier(params[2]) : QUANTIFIER_ALL;
this.altitude = this.parseAltitudeOrDefault(settings.getStringParam());
int count = settings.getFurniIds().length;
if (count > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
return false;
}
this.items.clear();
if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
if (room == null) {
return false;
}
for (int itemId : settings.getFurniIds()) {
HabboItem item = room.getHabboItem(itemId);
if (item != null) {
this.items.add(item);
}
}
}
return true;
}
private boolean matchesAltitude(HabboItem item) {
if (item == null) {
return false;
}
double normalizedAltitude = this.normalizeAltitude(item.getZ());
switch (this.comparison) {
case COMPARISON_LESS:
return normalizedAltitude < this.altitude;
case COMPARISON_GREATER:
return normalizedAltitude > this.altitude;
default:
return BigDecimal.valueOf(normalizedAltitude).compareTo(BigDecimal.valueOf(this.altitude)) == 0;
}
}
private void refresh(Room room) {
THashSet<HabboItem> remove = new THashSet<>();
for (HabboItem item : this.items) {
if (room.getHabboItem(item.getId()) == null) {
remove.add(item);
}
}
for (HabboItem item : remove) {
this.items.remove(item);
}
}
private int normalizeComparison(int value) {
if (value < COMPARISON_LESS || value > COMPARISON_GREATER) {
return COMPARISON_EQUAL;
}
return value;
}
private int normalizeQuantifier(int value) {
return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
}
private 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 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) {
if (value == null || value.trim().isEmpty()) {
return 0.0D;
}
try {
return this.normalizeAltitude(new BigDecimal(value.trim()).doubleValue());
} catch (NumberFormatException exception) {
return 0.0D;
}
}
private String formatAltitude(double value) {
BigDecimal decimal = BigDecimal.valueOf(this.normalizeAltitude(value)).stripTrailingZeros();
return (decimal.scale() < 0 ? decimal.setScale(0, RoundingMode.DOWN) : decimal).toPlainString();
}
static class JsonData {
int comparison;
String altitude;
int furniSource;
int quantifier;
List<Integer> itemIds;
public JsonData(int comparison, String altitude, int furniSource, int quantifier, List<Integer> itemIds) {
this.comparison = comparison;
this.altitude = altitude;
this.furniSource = furniSource;
this.quantifier = quantifier;
this.itemIds = itemIds;
}
}
}
@@ -0,0 +1,253 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition;
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.WiredContext;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.util.HotelDateTimeUtil;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
public class WiredConditionMatchDate extends InteractionWiredCondition {
private static final int MODE_SKIP = 0;
private static final int MODE_EXACT = 1;
private static final int MODE_RANGE = 2;
private static final int ALL_WEEKDAYS_MASK = createMask(1, 7);
private static final int ALL_MONTHS_MASK = createMask(1, 12);
public static final WiredConditionType type = WiredConditionType.MATCH_DATE;
private int weekdayMask = ALL_WEEKDAYS_MASK;
private int dayMode = MODE_SKIP;
private int dayFrom = 1;
private int dayTo = 31;
private int monthMask = ALL_MONTHS_MASK;
private int yearMode = MODE_SKIP;
private int yearFrom = HotelDateTimeUtil.localDateNow().getYear();
private int yearTo = HotelDateTimeUtil.localDateNow().getYear();
public WiredConditionMatchDate(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredConditionMatchDate(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public WiredConditionType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(false);
message.appendInt(5);
message.appendInt(0);
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(8);
message.appendInt(this.weekdayMask);
message.appendInt(this.dayMode);
message.appendInt(this.dayFrom);
message.appendInt(this.dayTo);
message.appendInt(this.monthMask);
message.appendInt(this.yearMode);
message.appendInt(this.yearFrom);
message.appendInt(this.yearTo);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(0);
message.appendInt(0);
}
@Override
public boolean saveData(WiredSettings settings) {
int[] params = settings.getIntParams();
this.weekdayMask = (params.length > 0) ? this.normalizeWeekdayMask(params[0]) : ALL_WEEKDAYS_MASK;
this.dayMode = (params.length > 1) ? this.normalizeMode(params[1]) : MODE_SKIP;
this.dayFrom = (params.length > 2) ? this.normalizeDay(params[2]) : 1;
this.dayTo = (params.length > 3) ? this.normalizeDay(params[3]) : this.dayFrom;
this.monthMask = (params.length > 4) ? this.normalizeMonthMask(params[4]) : ALL_MONTHS_MASK;
this.yearMode = (params.length > 5) ? this.normalizeMode(params[5]) : MODE_SKIP;
this.yearFrom = (params.length > 6) ? this.normalizeYear(params[6]) : HotelDateTimeUtil.localDateNow().getYear();
this.yearTo = (params.length > 7) ? this.normalizeYear(params[7]) : this.yearFrom;
return true;
}
@Override
public boolean evaluate(WiredContext ctx) {
LocalDate now = HotelDateTimeUtil.localDateNow();
return this.matchesMask(now.getDayOfWeek().getValue(), this.weekdayMask)
&& this.matchesMask(now.getMonthValue(), this.monthMask)
&& this.matchesDatePart(now.getDayOfMonth(), this.dayMode, this.dayFrom, this.dayTo)
&& this.matchesDatePart(now.getYear(), this.yearMode, this.yearFrom, this.yearTo);
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.weekdayMask,
this.dayMode,
this.dayFrom,
this.dayTo,
this.monthMask,
this.yearMode,
this.yearFrom,
this.yearTo
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.reset();
String wiredData = set.getString("wired_data");
if (wiredData == null || wiredData.isEmpty()) {
return;
}
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
if (data == null) {
return;
}
this.weekdayMask = this.normalizeWeekdayMask(data.weekdayMask);
this.dayMode = this.normalizeMode(data.dayMode);
this.dayFrom = this.normalizeDay(data.dayFrom);
this.dayTo = this.normalizeDay(data.dayTo);
this.monthMask = this.normalizeMonthMask(data.monthMask);
this.yearMode = this.normalizeMode(data.yearMode);
this.yearFrom = this.normalizeYear(data.yearFrom);
this.yearTo = this.normalizeYear(data.yearTo);
return;
}
String[] data = wiredData.split("\t");
if (data.length != 8) {
return;
}
try {
this.weekdayMask = this.normalizeWeekdayMask(Integer.parseInt(data[0]));
this.dayMode = this.normalizeMode(Integer.parseInt(data[1]));
this.dayFrom = this.normalizeDay(Integer.parseInt(data[2]));
this.dayTo = this.normalizeDay(Integer.parseInt(data[3]));
this.monthMask = this.normalizeMonthMask(Integer.parseInt(data[4]));
this.yearMode = this.normalizeMode(Integer.parseInt(data[5]));
this.yearFrom = this.normalizeYear(Integer.parseInt(data[6]));
this.yearTo = this.normalizeYear(Integer.parseInt(data[7]));
} catch (NumberFormatException ignored) {
this.reset();
}
}
@Override
public void onPickUp() {
this.reset();
}
private void reset() {
int currentYear = HotelDateTimeUtil.localDateNow().getYear();
this.weekdayMask = ALL_WEEKDAYS_MASK;
this.dayMode = MODE_SKIP;
this.dayFrom = 1;
this.dayTo = 31;
this.monthMask = ALL_MONTHS_MASK;
this.yearMode = MODE_SKIP;
this.yearFrom = currentYear;
this.yearTo = currentYear;
}
private boolean matchesMask(int value, int mask) {
return (mask & (1 << value)) != 0;
}
private boolean matchesDatePart(int currentValue, int mode, int fromValue, int toValue) {
switch (mode) {
case MODE_EXACT:
return currentValue == fromValue;
case MODE_RANGE:
return currentValue >= Math.min(fromValue, toValue) && currentValue <= Math.max(fromValue, toValue);
default:
return true;
}
}
private int normalizeMode(int value) {
if (value < MODE_SKIP || value > MODE_RANGE) {
return MODE_SKIP;
}
return value;
}
private int normalizeDay(int value) {
return Math.max(1, Math.min(31, value));
}
private int normalizeYear(int value) {
return Math.max(1, Math.min(9999, value));
}
private int normalizeWeekdayMask(int value) {
int normalized = value & ALL_WEEKDAYS_MASK;
return (normalized == 0) ? ALL_WEEKDAYS_MASK : normalized;
}
private int normalizeMonthMask(int value) {
int normalized = value & ALL_MONTHS_MASK;
return (normalized == 0) ? ALL_MONTHS_MASK : normalized;
}
private static int createMask(int startValue, int endValue) {
int mask = 0;
for (int value = startValue; value <= endValue; value++) {
mask |= (1 << value);
}
return mask;
}
static class JsonData {
int weekdayMask;
int dayMode;
int dayFrom;
int dayTo;
int monthMask;
int yearMode;
int yearFrom;
int yearTo;
public JsonData(int weekdayMask, int dayMode, int dayFrom, int dayTo, int monthMask, int yearMode, int yearFrom, int yearTo) {
this.weekdayMask = weekdayMask;
this.dayMode = dayMode;
this.dayFrom = dayFrom;
this.dayTo = dayTo;
this.monthMask = monthMask;
this.yearMode = yearMode;
this.yearFrom = yearFrom;
this.yearTo = yearTo;
}
}
}
@@ -19,9 +19,7 @@ import gnu.trove.set.hash.THashSet;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class WiredConditionMatchStatePosition extends InteractionWiredCondition implements InteractionWiredMatchFurniSettings {
public static final WiredConditionType type = WiredConditionType.MATCH_SSHOT;
@@ -92,7 +90,6 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition
this.settings.clear();
if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
for (int i = 0; i < count; i++) {
int itemId = settings.getFurniIds()[i];
HabboItem item = room.getHabboItem(itemId);
@@ -100,7 +97,6 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition
if (item != null)
this.settings.add(new WiredMatchFurniSetting(item.getId(), item.getExtradata(), item.getRotation(), item.getX(), item.getY()));
}
}
return true;
}
@@ -108,65 +104,71 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition
@Override
public boolean evaluate(WiredContext ctx) {
Room room = ctx.room();
this.refresh();
if (this.settings.isEmpty())
return true;
List<HabboItem> targets = null;
Set<Integer> targetIds = null;
if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) {
targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, null);
List<HabboItem> targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, null);
if (targets.isEmpty()) return false;
targetIds = new HashSet<>();
for (HabboItem item : targets) {
if (item != null) targetIds.add(item.getId());
}
if (targetIds.isEmpty()) return false;
}
if (item == null) return false;
THashSet<WiredMatchFurniSetting> toRemove = new THashSet<>();
Set<Integer> settingsIds = new HashSet<>();
for (WiredMatchFurniSetting setting : this.settings) {
if (targetIds != null && !targetIds.contains(setting.item_id)) {
continue;
}
HabboItem item = room.getHabboItem(setting.item_id);
if (item != null) {
settingsIds.add(setting.item_id);
if (this.state) {
if (!item.getExtradata().equals(setting.state))
WiredMatchFurniSetting setting = this.resolveSettingForTarget(room, item);
if (setting == null) {
return false;
}
if (this.position) {
if (!(setting.x == item.getX() && setting.y == item.getY()))
if (!this.matchesSetting(item, setting)) {
return false;
}
if (this.direction) {
if (setting.rotation != item.getRotation())
return false;
}
} else {
toRemove.add(setting);
}
}
if (targetIds != null && !settingsIds.containsAll(targetIds)) {
return false;
}
if (!toRemove.isEmpty()) {
for (WiredMatchFurniSetting setting : toRemove) {
this.settings.remove(setting);
}
}
return true;
}
for (WiredMatchFurniSetting setting : this.settings) {
HabboItem item = room.getHabboItem(setting.item_id);
if (item == null) continue;
if (!this.matchesSetting(item, setting))
return false;
}
return true;
}
private WiredMatchFurniSetting resolveSettingForTarget(Room room, HabboItem target) {
WiredMatchFurniSetting fallback = null;
for (WiredMatchFurniSetting setting : this.settings) {
HabboItem sourceItem = room.getHabboItem(setting.item_id);
if (sourceItem == null) continue;
if (sourceItem.getBaseItem().getId() != target.getBaseItem().getId()) continue;
if (setting.state.equals(target.getExtradata())) {
return setting;
}
if (fallback == null) {
fallback = setting;
}
}
return fallback;
}
private boolean matchesSetting(HabboItem item, WiredMatchFurniSetting setting) {
if (this.state && !item.getExtradata().equals(setting.state))
return false;
if (this.position && !(setting.x == item.getX() && setting.y == item.getY()))
return false;
return !this.direction || setting.rotation == item.getRotation();
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
@@ -214,9 +216,6 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition
this.position = data[4].equals("1");
this.furniSource = this.settings.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED;
}
if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.settings.isEmpty()) {
this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
}
}
@Override
@@ -0,0 +1,237 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition;
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.WiredContext;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.util.HotelDateTimeUtil;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalTime;
public class WiredConditionMatchTime extends InteractionWiredCondition {
private static final int MODE_SKIP = 0;
private static final int MODE_EXACT = 1;
private static final int MODE_RANGE = 2;
public static final WiredConditionType type = WiredConditionType.MATCH_TIME;
private int hourMode = MODE_SKIP;
private int hourFrom = 0;
private int hourTo = 0;
private int minuteMode = MODE_SKIP;
private int minuteFrom = 0;
private int minuteTo = 0;
private int secondMode = MODE_SKIP;
private int secondFrom = 0;
private int secondTo = 0;
public WiredConditionMatchTime(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredConditionMatchTime(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public WiredConditionType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(false);
message.appendInt(5);
message.appendInt(0);
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(9);
message.appendInt(this.hourMode);
message.appendInt(this.hourFrom);
message.appendInt(this.hourTo);
message.appendInt(this.minuteMode);
message.appendInt(this.minuteFrom);
message.appendInt(this.minuteTo);
message.appendInt(this.secondMode);
message.appendInt(this.secondFrom);
message.appendInt(this.secondTo);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(0);
message.appendInt(0);
}
@Override
public boolean saveData(WiredSettings settings) {
int[] params = settings.getIntParams();
this.hourMode = (params.length > 0) ? this.normalizeMode(params[0]) : MODE_SKIP;
this.hourFrom = (params.length > 1) ? this.normalizeHour(params[1]) : 0;
this.hourTo = (params.length > 2) ? this.normalizeHour(params[2]) : this.hourFrom;
this.minuteMode = (params.length > 3) ? this.normalizeMode(params[3]) : MODE_SKIP;
this.minuteFrom = (params.length > 4) ? this.normalizeMinuteOrSecond(params[4]) : 0;
this.minuteTo = (params.length > 5) ? this.normalizeMinuteOrSecond(params[5]) : this.minuteFrom;
this.secondMode = (params.length > 6) ? this.normalizeMode(params[6]) : MODE_SKIP;
this.secondFrom = (params.length > 7) ? this.normalizeMinuteOrSecond(params[7]) : 0;
this.secondTo = (params.length > 8) ? this.normalizeMinuteOrSecond(params[8]) : this.secondFrom;
return true;
}
@Override
public boolean evaluate(WiredContext ctx) {
LocalTime now = HotelDateTimeUtil.localTimeNow();
return this.matchesTimePart(now.getHour(), this.hourMode, this.hourFrom, this.hourTo)
&& this.matchesTimePart(now.getMinute(), this.minuteMode, this.minuteFrom, this.minuteTo)
&& this.matchesTimePart(now.getSecond(), this.secondMode, this.secondFrom, this.secondTo);
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.hourMode,
this.hourFrom,
this.hourTo,
this.minuteMode,
this.minuteFrom,
this.minuteTo,
this.secondMode,
this.secondFrom,
this.secondTo
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.reset();
String wiredData = set.getString("wired_data");
if (wiredData == null || wiredData.isEmpty()) {
return;
}
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
if (data == null) {
return;
}
this.hourMode = this.normalizeMode(data.hourMode);
this.hourFrom = this.normalizeHour(data.hourFrom);
this.hourTo = this.normalizeHour(data.hourTo);
this.minuteMode = this.normalizeMode(data.minuteMode);
this.minuteFrom = this.normalizeMinuteOrSecond(data.minuteFrom);
this.minuteTo = this.normalizeMinuteOrSecond(data.minuteTo);
this.secondMode = this.normalizeMode(data.secondMode);
this.secondFrom = this.normalizeMinuteOrSecond(data.secondFrom);
this.secondTo = this.normalizeMinuteOrSecond(data.secondTo);
return;
}
String[] data = wiredData.split("\t");
if (data.length != 9) {
return;
}
try {
this.hourMode = this.normalizeMode(Integer.parseInt(data[0]));
this.hourFrom = this.normalizeHour(Integer.parseInt(data[1]));
this.hourTo = this.normalizeHour(Integer.parseInt(data[2]));
this.minuteMode = this.normalizeMode(Integer.parseInt(data[3]));
this.minuteFrom = this.normalizeMinuteOrSecond(Integer.parseInt(data[4]));
this.minuteTo = this.normalizeMinuteOrSecond(Integer.parseInt(data[5]));
this.secondMode = this.normalizeMode(Integer.parseInt(data[6]));
this.secondFrom = this.normalizeMinuteOrSecond(Integer.parseInt(data[7]));
this.secondTo = this.normalizeMinuteOrSecond(Integer.parseInt(data[8]));
} catch (NumberFormatException ignored) {
this.reset();
}
}
@Override
public void onPickUp() {
this.reset();
}
private void reset() {
this.hourMode = MODE_SKIP;
this.hourFrom = 0;
this.hourTo = 0;
this.minuteMode = MODE_SKIP;
this.minuteFrom = 0;
this.minuteTo = 0;
this.secondMode = MODE_SKIP;
this.secondFrom = 0;
this.secondTo = 0;
}
private boolean matchesTimePart(int currentValue, int mode, int fromValue, int toValue) {
switch (mode) {
case MODE_EXACT:
return currentValue == fromValue;
case MODE_RANGE:
if (fromValue <= toValue) {
return currentValue >= fromValue && currentValue <= toValue;
}
return currentValue >= fromValue || currentValue <= toValue;
default:
return true;
}
}
private int normalizeMode(int value) {
if (value < MODE_SKIP || value > MODE_RANGE) {
return MODE_SKIP;
}
return value;
}
private int normalizeHour(int value) {
return Math.max(0, Math.min(23, value));
}
private int normalizeMinuteOrSecond(int value) {
return Math.max(0, Math.min(59, value));
}
static class JsonData {
int hourMode;
int hourFrom;
int hourTo;
int minuteMode;
int minuteFrom;
int minuteTo;
int secondMode;
int secondFrom;
int secondTo;
public JsonData(int hourMode, int hourFrom, int hourTo, int minuteMode, int minuteFrom, int minuteTo, int secondMode, int secondFrom, int secondTo) {
this.hourMode = hourMode;
this.hourFrom = hourFrom;
this.hourTo = hourTo;
this.minuteMode = minuteMode;
this.minuteFrom = minuteFrom;
this.minuteTo = minuteTo;
this.secondMode = secondMode;
this.secondFrom = secondFrom;
this.secondTo = secondTo;
}
}
}
@@ -0,0 +1,42 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.wired.WiredConditionType;
import com.eu.habbo.habbohotel.wired.core.WiredContext;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class WiredConditionNotHabboHasHandItem extends WiredConditionHabboHasHandItem {
public static final WiredConditionType type = WiredConditionType.NOT_ACTOR_HAS_HANDITEM;
public WiredConditionNotHabboHasHandItem(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredConditionNotHabboHasHandItem(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public boolean evaluate(WiredContext ctx) {
List<RoomUnit> targets = WiredSourceUtil.resolveUsers(ctx, this.getUserSource());
if (targets.isEmpty()) return false;
for (RoomUnit roomUnit : targets) {
if (roomUnit == null || roomUnit.getHandItem() == this.getHandItem()) {
return false;
}
}
return true;
}
@Override
public WiredConditionType getType() {
return type;
}
}
@@ -0,0 +1,31 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.wired.WiredConditionType;
import com.eu.habbo.habbohotel.wired.core.WiredContext;
import java.sql.ResultSet;
import java.sql.SQLException;
public class WiredConditionNotTriggererMatch extends WiredConditionTriggererMatch {
public static final WiredConditionType type = WiredConditionType.NOT_TRIGGERER_MATCH;
public WiredConditionNotTriggererMatch(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredConditionNotTriggererMatch(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public boolean evaluate(WiredContext ctx) {
MatchResult result = this.evaluateMatch(ctx);
return result.valid && !result.matched;
}
@Override
public WiredConditionType getType() {
return type;
}
}
@@ -0,0 +1,46 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.wired.WiredConditionType;
import com.eu.habbo.habbohotel.wired.core.WiredContext;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class WiredConditionNotUserPerformsAction extends WiredConditionUserPerformsAction {
private static final int QUANTIFIER_ANY_NOT_MATCH = 0;
private static final int QUANTIFIER_NONE_MATCH = 1;
public static final WiredConditionType type = WiredConditionType.NOT_USER_PERFORMS_ACTION;
public WiredConditionNotUserPerformsAction(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredConditionNotUserPerformsAction(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public boolean evaluate(WiredContext ctx) {
List<RoomUnit> targets = WiredSourceUtil.resolveUsers(ctx, this.getUserSource());
if (targets.isEmpty()) {
return false;
}
if (this.getQuantifier() == QUANTIFIER_NONE_MATCH) {
return targets.stream().noneMatch(roomUnit -> this.matchesAction(ctx, roomUnit));
}
return targets.stream().anyMatch(roomUnit -> !this.matchesAction(ctx, roomUnit));
}
@Override
public WiredConditionType getType() {
return type;
}
}
@@ -0,0 +1,197 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.games.Game;
import com.eu.habbo.habbohotel.games.GameState;
import com.eu.habbo.habbohotel.games.GameTeam;
import com.eu.habbo.habbohotel.games.GameTeamColors;
import com.eu.habbo.habbohotel.games.battlebanzai.BattleBanzaiGame;
import com.eu.habbo.habbohotel.games.freeze.FreezeGame;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.wired.core.WiredContext;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
abstract class WiredConditionTeamGameBase extends InteractionWiredCondition {
protected static final int QUANTIFIER_ALL = 0;
protected static final int QUANTIFIER_ANY = 1;
protected static final int COMPARISON_LOWER = 0;
protected static final int COMPARISON_EQUAL = 1;
protected static final int COMPARISON_HIGHER = 2;
protected static final int TEAM_TRIGGERER = 0;
private static final GameTeamColors[] SUPPORTED_TEAM_COLORS = new GameTeamColors[] {
GameTeamColors.RED,
GameTeamColors.GREEN,
GameTeamColors.BLUE,
GameTeamColors.YELLOW
};
protected WiredConditionTeamGameBase(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
protected WiredConditionTeamGameBase(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
protected List<RoomUnit> resolveUsers(WiredContext ctx, int userSource) {
Map<Integer, RoomUnit> deduplicated = new LinkedHashMap<>();
for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, userSource)) {
if (roomUnit != null) {
deduplicated.putIfAbsent(roomUnit.getId(), roomUnit);
}
}
return new ArrayList<>(deduplicated.values());
}
protected boolean matchesQuantifier(List<RoomUnit> users, int quantifier, Predicate<RoomUnit> predicate) {
if (users.isEmpty()) {
return false;
}
if (quantifier == QUANTIFIER_ANY) {
return users.stream().anyMatch(predicate);
}
return users.stream().allMatch(predicate);
}
protected int normalizeQuantifier(int value) {
return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
}
protected int normalizeComparison(int value) {
switch (value) {
case COMPARISON_LOWER:
case COMPARISON_HIGHER:
return value;
default:
return COMPARISON_EQUAL;
}
}
protected int normalizeUserSource(int value) {
switch (value) {
case WiredSourceUtil.SOURCE_SELECTOR:
case WiredSourceUtil.SOURCE_SIGNAL:
case WiredSourceUtil.SOURCE_TRIGGER:
return value;
default:
return WiredSourceUtil.SOURCE_TRIGGER;
}
}
protected int normalizePlacement(int value) {
if (value < 1 || value > 4) {
return 1;
}
return value;
}
protected int normalizeScore(int value) {
return Math.max(0, value);
}
protected int normalizeExplicitTeamType(int value) {
GameTeamColors color = GameTeamColors.fromType(value);
return (color.type >= GameTeamColors.RED.type && color.type <= GameTeamColors.YELLOW.type)
? color.type
: GameTeamColors.RED.type;
}
protected int normalizeRankTeamType(int value) {
if (value == TEAM_TRIGGERER) {
return TEAM_TRIGGERER;
}
return this.normalizeExplicitTeamType(value);
}
protected GameTeamColors resolveConfiguredTeamColor(int value) {
return GameTeamColors.fromType(this.normalizeExplicitTeamType(value));
}
protected boolean compareValue(int actual, int expected, int comparison) {
switch (comparison) {
case COMPARISON_LOWER:
return actual < expected;
case COMPARISON_HIGHER:
return actual > expected;
default:
return actual == expected;
}
}
protected UserGameContext resolveUserGameContext(Room room, RoomUnit roomUnit) {
if (room == null || roomUnit == null) {
return null;
}
Habbo habbo = room.getHabbo(roomUnit);
if (habbo == null || habbo.getHabboInfo() == null || habbo.getHabboInfo().getCurrentGame() == null) {
return null;
}
Game game = room.getGame(habbo.getHabboInfo().getCurrentGame());
if (!this.isSupportedGame(game)) {
return null;
}
GameTeam team = game.getTeamForHabbo(habbo);
if (team == null) {
return null;
}
return new UserGameContext(habbo, game, team);
}
protected int getTeamRank(Game game, GameTeam team) {
if (game == null || team == null) {
return Integer.MAX_VALUE;
}
int rank = 1;
int targetScore = team.getTotalScore();
for (GameTeamColors teamColor : SUPPORTED_TEAM_COLORS) {
GameTeam otherTeam = game.getTeam(teamColor);
if (otherTeam != null && otherTeam != team && otherTeam.getTotalScore() > targetScore) {
rank++;
}
}
return rank;
}
private boolean isSupportedGame(Game game) {
return game != null
&& game.getState() != GameState.IDLE
&& (game instanceof FreezeGame || game instanceof BattleBanzaiGame);
}
protected static class UserGameContext {
protected final Habbo habbo;
protected final Game game;
protected final GameTeam team;
protected UserGameContext(Habbo habbo, Game game, GameTeam team) {
this.habbo = habbo;
this.game = game;
this.team = team;
}
}
}
@@ -0,0 +1,175 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.games.GameTeam;
import com.eu.habbo.habbohotel.games.GameTeamColors;
import com.eu.habbo.habbohotel.items.Item;
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.WiredContext;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import com.eu.habbo.messages.ServerMessage;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class WiredConditionTeamHasRank extends WiredConditionTeamGameBase {
public static final WiredConditionType type = WiredConditionType.TEAM_HAS_RANK;
private int teamType = GameTeamColors.RED.type;
private int placement = 1;
private int userSource = WiredSourceUtil.SOURCE_TRIGGER;
private int quantifier = QUANTIFIER_ALL;
public WiredConditionTeamHasRank(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredConditionTeamHasRank(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public boolean evaluate(WiredContext ctx) {
Room room = ctx.room();
List<RoomUnit> users = this.resolveUsers(ctx, this.userSource);
return this.matchesQuantifier(users, this.quantifier, roomUnit -> this.matchesUser(ctx, room, roomUnit));
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.teamType,
this.placement,
this.userSource,
this.quantifier
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.resetSettings();
String wiredData = set.getString("wired_data");
if (wiredData == null || !wiredData.startsWith("{")) {
return;
}
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
if (data == null) {
return;
}
this.teamType = this.normalizeRankTeamType(data.teamType);
this.placement = this.normalizePlacement(data.placement);
this.userSource = this.normalizeUserSource(data.userSource);
this.quantifier = this.normalizeQuantifier(data.quantifier);
}
@Override
public void onPickUp() {
this.resetSettings();
}
@Override
public WiredConditionType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(false);
message.appendInt(5);
message.appendInt(0);
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(4);
message.appendInt(this.teamType);
message.appendInt(this.placement);
message.appendInt(this.userSource);
message.appendInt(this.quantifier);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(0);
message.appendInt(0);
}
@Override
public boolean saveData(WiredSettings settings) {
int[] params = settings.getIntParams();
this.resetSettings();
if (params.length > 0) this.teamType = this.normalizeRankTeamType(params[0]);
if (params.length > 1) this.placement = this.normalizePlacement(params[1]);
if (params.length > 2) this.userSource = this.normalizeUserSource(params[2]);
if (params.length > 3) this.quantifier = this.normalizeQuantifier(params[3]);
return true;
}
private boolean matchesUser(WiredContext ctx, Room room, RoomUnit roomUnit) {
UserGameContext context = this.resolveUserGameContext(room, roomUnit);
if (context == null) {
return false;
}
GameTeamColors requiredTeam = this.resolveRequiredTeamColor(ctx, room, context.game);
if (requiredTeam == GameTeamColors.NONE || context.team.teamColor != requiredTeam) {
return false;
}
GameTeam team = context.game.getTeam(requiredTeam);
if (team == null) {
return false;
}
return this.getTeamRank(context.game, team) == this.placement;
}
private GameTeamColors resolveRequiredTeamColor(WiredContext ctx, Room room, com.eu.habbo.habbohotel.games.Game game) {
if (this.teamType == TEAM_TRIGGERER) {
RoomUnit actor = ctx.actor().orElse(null);
UserGameContext triggererContext = this.resolveUserGameContext(room, actor);
if (triggererContext == null || triggererContext.game != game) {
return GameTeamColors.NONE;
}
return triggererContext.team.teamColor;
}
return this.resolveConfiguredTeamColor(this.teamType);
}
private void resetSettings() {
this.teamType = GameTeamColors.RED.type;
this.placement = 1;
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = QUANTIFIER_ALL;
}
static class JsonData {
int teamType;
int placement;
int userSource;
int quantifier;
public JsonData(int teamType, int placement, int userSource, int quantifier) {
this.teamType = teamType;
this.placement = placement;
this.userSource = userSource;
this.quantifier = quantifier;
}
}
}
@@ -0,0 +1,162 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.games.GameTeamColors;
import com.eu.habbo.habbohotel.items.Item;
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.WiredContext;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import com.eu.habbo.messages.ServerMessage;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class WiredConditionTeamHasScore extends WiredConditionTeamGameBase {
public static final WiredConditionType type = WiredConditionType.TEAM_HAS_SCORE;
private int teamType = GameTeamColors.RED.type;
private int comparison = COMPARISON_EQUAL;
private int score = 0;
private int userSource = WiredSourceUtil.SOURCE_TRIGGER;
private int quantifier = QUANTIFIER_ALL;
public WiredConditionTeamHasScore(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredConditionTeamHasScore(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public boolean evaluate(WiredContext ctx) {
Room room = ctx.room();
List<RoomUnit> users = this.resolveUsers(ctx, this.userSource);
return this.matchesQuantifier(users, this.quantifier, roomUnit -> this.matchesUser(room, roomUnit));
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.teamType,
this.comparison,
this.score,
this.userSource,
this.quantifier
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.resetSettings();
String wiredData = set.getString("wired_data");
if (wiredData == null || !wiredData.startsWith("{")) {
return;
}
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
if (data == null) {
return;
}
this.teamType = this.normalizeExplicitTeamType(data.teamType);
this.comparison = this.normalizeComparison(data.comparison);
this.score = this.normalizeScore(data.score);
this.userSource = this.normalizeUserSource(data.userSource);
this.quantifier = this.normalizeQuantifier(data.quantifier);
}
@Override
public void onPickUp() {
this.resetSettings();
}
@Override
public WiredConditionType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(false);
message.appendInt(5);
message.appendInt(0);
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(5);
message.appendInt(this.teamType);
message.appendInt(this.comparison);
message.appendInt(this.score);
message.appendInt(this.userSource);
message.appendInt(this.quantifier);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(0);
message.appendInt(0);
}
@Override
public boolean saveData(WiredSettings settings) {
int[] params = settings.getIntParams();
this.resetSettings();
if (params.length > 0) this.teamType = this.normalizeExplicitTeamType(params[0]);
if (params.length > 1) this.comparison = this.normalizeComparison(params[1]);
if (params.length > 2) this.score = this.normalizeScore(params[2]);
if (params.length > 3) this.userSource = this.normalizeUserSource(params[3]);
if (params.length > 4) this.quantifier = this.normalizeQuantifier(params[4]);
return true;
}
private boolean matchesUser(Room room, RoomUnit roomUnit) {
UserGameContext context = this.resolveUserGameContext(room, roomUnit);
if (context == null) {
return false;
}
GameTeamColors requiredTeam = this.resolveConfiguredTeamColor(this.teamType);
if (context.team.teamColor != requiredTeam) {
return false;
}
return this.compareValue(context.team.getTotalScore(), this.score, this.comparison);
}
private void resetSettings() {
this.teamType = GameTeamColors.RED.type;
this.comparison = COMPARISON_EQUAL;
this.score = 0;
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = QUANTIFIER_ALL;
}
static class JsonData {
int teamType;
int comparison;
int score;
int userSource;
int quantifier;
public JsonData(int teamType, int comparison, int score, int userSource, int quantifier) {
this.teamType = teamType;
this.comparison = comparison;
this.score = score;
this.userSource = userSource;
this.quantifier = quantifier;
}
}
}
@@ -0,0 +1,368 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.bots.Bot;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.pets.Pet;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.rooms.RoomUnitType;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.wired.WiredConditionType;
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;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class WiredConditionTriggererMatch extends InteractionWiredCondition {
protected static final int ENTITY_HABBO = 1;
protected static final int ENTITY_PET = 2;
protected static final int ENTITY_BOT = 4;
protected static final int AVATAR_MODE_ANY = 0;
protected static final int AVATAR_MODE_CERTAIN = 1;
protected static final int QUANTIFIER_ALL = 0;
protected static final int QUANTIFIER_ANY = 1;
protected static final int SOURCE_SPECIFIED_USERNAME = 101;
public static final WiredConditionType type = WiredConditionType.TRIGGERER_MATCH;
private int entityType = ENTITY_HABBO;
private int avatarMode = AVATAR_MODE_ANY;
private int matchUserSource = WiredSourceUtil.SOURCE_TRIGGER;
private int compareUserSource = WiredSourceUtil.SOURCE_TRIGGER;
private int quantifier = QUANTIFIER_ALL;
private String username = "";
public WiredConditionTriggererMatch(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredConditionTriggererMatch(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public boolean evaluate(WiredContext ctx) {
MatchResult result = this.evaluateMatch(ctx);
return result.valid && result.matched;
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.entityType,
this.avatarMode,
this.matchUserSource,
this.compareUserSource,
this.quantifier,
this.username
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.resetSettings();
String wiredData = set.getString("wired_data");
if (wiredData == null || !wiredData.startsWith("{")) {
return;
}
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
if (data == null) {
return;
}
this.entityType = this.normalizeEntityType(data.entityType);
this.avatarMode = this.normalizeAvatarMode(data.avatarMode);
this.matchUserSource = this.normalizePrimaryUserSource(data.matchUserSource);
this.compareUserSource = this.normalizeCompareUserSource(data.compareUserSource);
this.quantifier = this.normalizeQuantifier(data.quantifier);
this.username = this.normalizeUsername(data.username);
}
@Override
public void onPickUp() {
this.resetSettings();
}
@Override
public WiredConditionType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(true);
message.appendInt(5);
message.appendInt(0);
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString(this.username);
message.appendInt(5);
message.appendInt(this.entityType);
message.appendInt(this.avatarMode);
message.appendInt(this.matchUserSource);
message.appendInt(this.compareUserSource);
message.appendInt(this.quantifier);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(0);
message.appendInt(0);
}
@Override
public boolean saveData(WiredSettings settings) {
int[] params = settings.getIntParams();
this.resetSettings();
if (params.length > 0) this.entityType = this.normalizeEntityType(params[0]);
if (params.length > 1) this.avatarMode = this.normalizeAvatarMode(params[1]);
if (params.length > 2) this.matchUserSource = this.normalizePrimaryUserSource(params[2]);
if (params.length > 3) this.compareUserSource = this.normalizeCompareUserSource(params[3]);
if (params.length > 4) this.quantifier = this.normalizeQuantifier(params[4]);
this.username = this.normalizeUsername(settings.getStringParam());
return true;
}
protected MatchResult evaluateMatch(WiredContext ctx) {
List<RoomUnit> matchUsers = this.resolvePrimaryUsers(ctx);
if (matchUsers.isEmpty()) {
return MatchResult.invalid();
}
List<RoomUnit> compareUsers = this.resolveCompareUsers(ctx);
if (compareUsers.isEmpty()) {
return MatchResult.valid(false);
}
Set<Integer> compareUserIds = compareUsers.stream()
.filter(this::matchesEntityType)
.map(RoomUnit::getId)
.collect(Collectors.toSet());
if (compareUserIds.isEmpty()) {
return MatchResult.valid(false);
}
boolean matched;
if (this.quantifier == QUANTIFIER_ANY) {
matched = matchUsers.stream().anyMatch(roomUnit -> this.matchesCandidate(roomUnit, compareUserIds));
} else {
matched = matchUsers.stream().allMatch(roomUnit -> this.matchesCandidate(roomUnit, compareUserIds));
}
return MatchResult.valid(matched);
}
protected int getQuantifier() {
return this.quantifier;
}
private void resetSettings() {
this.entityType = ENTITY_HABBO;
this.avatarMode = AVATAR_MODE_ANY;
this.matchUserSource = WiredSourceUtil.SOURCE_TRIGGER;
this.compareUserSource = WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = QUANTIFIER_ALL;
this.username = "";
}
private List<RoomUnit> resolvePrimaryUsers(WiredContext ctx) {
return this.deduplicate(WiredSourceUtil.resolveUsers(ctx, this.matchUserSource));
}
private List<RoomUnit> resolveCompareUsers(WiredContext ctx) {
List<RoomUnit> resolved;
if (this.compareUserSource == SOURCE_SPECIFIED_USERNAME) {
resolved = this.resolveUsersByName(ctx.room(), this.username);
} else {
resolved = WiredSourceUtil.resolveUsers(ctx, this.compareUserSource);
}
if (this.avatarMode == AVATAR_MODE_CERTAIN) {
String normalizedName = this.normalizeUsername(this.username);
if (normalizedName.isEmpty()) {
return new ArrayList<>();
}
resolved = resolved.stream()
.filter(roomUnit -> normalizedName.equalsIgnoreCase(this.getRoomUnitName(ctx.room(), roomUnit)))
.collect(Collectors.toList());
}
return this.deduplicate(resolved);
}
private List<RoomUnit> resolveUsersByName(Room room, String username) {
List<RoomUnit> result = new ArrayList<>();
String normalizedName = this.normalizeUsername(username);
if (room == null || normalizedName.isEmpty()) {
return result;
}
Habbo habbo = room.getHabbo(normalizedName);
if (habbo != null && habbo.getRoomUnit() != null) {
result.add(habbo.getRoomUnit());
}
for (Bot bot : room.getBots(normalizedName)) {
if (bot != null && bot.getRoomUnit() != null) {
result.add(bot.getRoomUnit());
}
}
for (Pet pet : room.getUnitManager().getPets()) {
if (pet != null && pet.getRoomUnit() != null && normalizedName.equalsIgnoreCase(pet.getName())) {
result.add(pet.getRoomUnit());
}
}
return result;
}
private List<RoomUnit> deduplicate(List<RoomUnit> users) {
Map<Integer, RoomUnit> deduplicated = new LinkedHashMap<>();
for (RoomUnit user : users) {
if (user != null) {
deduplicated.putIfAbsent(user.getId(), user);
}
}
return new ArrayList<>(deduplicated.values());
}
private boolean matchesCandidate(RoomUnit roomUnit, Set<Integer> compareUserIds) {
return roomUnit != null && this.matchesEntityType(roomUnit) && compareUserIds.contains(roomUnit.getId());
}
private boolean matchesEntityType(RoomUnit roomUnit) {
return roomUnit != null && roomUnit.getRoomUnitType().getTypeId() == this.entityType;
}
private String getRoomUnitName(Room room, RoomUnit roomUnit) {
if (room == null || roomUnit == null) {
return "";
}
if (roomUnit.getRoomUnitType() == RoomUnitType.USER) {
Habbo habbo = room.getHabbo(roomUnit);
return (habbo != null && habbo.getHabboInfo() != null) ? habbo.getHabboInfo().getUsername() : "";
}
if (roomUnit.getRoomUnitType() == RoomUnitType.BOT) {
Bot bot = room.getBot(roomUnit);
return (bot != null) ? bot.getName() : "";
}
if (roomUnit.getRoomUnitType() == RoomUnitType.PET) {
Pet pet = room.getPet(roomUnit);
return (pet != null) ? pet.getName() : "";
}
return "";
}
private int normalizeEntityType(int value) {
switch (value) {
case ENTITY_HABBO:
case ENTITY_PET:
case ENTITY_BOT:
return value;
default:
return ENTITY_HABBO;
}
}
private int normalizeAvatarMode(int value) {
return (value == AVATAR_MODE_CERTAIN) ? AVATAR_MODE_CERTAIN : AVATAR_MODE_ANY;
}
private int normalizeQuantifier(int value) {
return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
}
private int normalizePrimaryUserSource(int value) {
switch (value) {
case WiredSourceUtil.SOURCE_SELECTOR:
case WiredSourceUtil.SOURCE_SIGNAL:
case WiredSourceUtil.SOURCE_TRIGGER:
return value;
default:
return WiredSourceUtil.SOURCE_TRIGGER;
}
}
private int normalizeCompareUserSource(int value) {
switch (value) {
case WiredSourceUtil.SOURCE_SELECTOR:
case WiredSourceUtil.SOURCE_SIGNAL:
case WiredSourceUtil.SOURCE_TRIGGER:
case SOURCE_SPECIFIED_USERNAME:
return value;
default:
return WiredSourceUtil.SOURCE_TRIGGER;
}
}
private String normalizeUsername(String value) {
return (value == null) ? "" : value.trim();
}
protected static class MatchResult {
protected final boolean valid;
protected final boolean matched;
private MatchResult(boolean valid, boolean matched) {
this.valid = valid;
this.matched = matched;
}
private static MatchResult invalid() {
return new MatchResult(false, false);
}
private static MatchResult valid(boolean matched) {
return new MatchResult(true, matched);
}
}
static class JsonData {
int entityType;
int avatarMode;
int matchUserSource;
int compareUserSource;
int quantifier;
String username;
public JsonData(int entityType, int avatarMode, int matchUserSource, int compareUserSource, int quantifier, String username) {
this.entityType = entityType;
this.avatarMode = avatarMode;
this.matchUserSource = matchUserSource;
this.compareUserSource = compareUserSource;
this.quantifier = quantifier;
this.username = username;
}
}
}
@@ -0,0 +1,346 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition;
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.rooms.RoomUnitStatus;
import com.eu.habbo.habbohotel.wired.WiredConditionType;
import com.eu.habbo.habbohotel.wired.WiredUserActionType;
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;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class WiredConditionUserPerformsAction extends InteractionWiredCondition {
private static final String CACHE_LAST_ACTION_ID = "wired.last_user_action.id";
private static final String CACHE_LAST_ACTION_PARAMETER = "wired.last_user_action.parameter";
private static final String CACHE_LAST_ACTION_TIMESTAMP = "wired.last_user_action.timestamp";
private static final long TRANSIENT_ACTION_WINDOW_MS = 5_000L;
protected static final int DEFAULT_ACTION = WiredUserActionType.WAVE;
protected static final int QUANTIFIER_ALL = 0;
protected static final int QUANTIFIER_ANY = 1;
public static final WiredConditionType type = WiredConditionType.USER_PERFORMS_ACTION;
private int selectedAction = DEFAULT_ACTION;
private boolean signFilterEnabled = false;
private int signId = 0;
private boolean danceFilterEnabled = false;
private int danceId = 1;
private int userSource = WiredSourceUtil.SOURCE_TRIGGER;
private int quantifier = QUANTIFIER_ALL;
public WiredConditionUserPerformsAction(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredConditionUserPerformsAction(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public boolean evaluate(WiredContext ctx) {
List<RoomUnit> targets = WiredSourceUtil.resolveUsers(ctx, this.userSource);
if (targets.isEmpty()) {
return false;
}
if (this.quantifier == QUANTIFIER_ANY) {
return targets.stream().anyMatch(roomUnit -> this.matchesAction(ctx, roomUnit));
}
return targets.stream().allMatch(roomUnit -> this.matchesAction(ctx, roomUnit));
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.selectedAction,
this.signFilterEnabled,
this.signId,
this.danceFilterEnabled,
this.danceId,
this.userSource,
this.quantifier
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.resetSettings();
String wiredData = set.getString("wired_data");
if (wiredData == null || !wiredData.startsWith("{")) {
return;
}
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
if (data == null) {
return;
}
this.selectedAction = normalizeAction(data.selectedAction);
this.signFilterEnabled = data.signFilterEnabled;
this.signId = normalizeSignId(data.signId);
this.danceFilterEnabled = data.danceFilterEnabled;
this.danceId = normalizeDanceId(data.danceId);
this.userSource = this.normalizeUserSource(data.userSource);
this.quantifier = normalizeQuantifier(data.quantifier);
}
@Override
public void onPickUp() {
this.resetSettings();
}
@Override
public WiredConditionType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(true);
message.appendInt(5);
message.appendInt(0);
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(7);
message.appendInt(this.selectedAction);
message.appendInt(this.signFilterEnabled ? 1 : 0);
message.appendInt(this.signId);
message.appendInt(this.danceFilterEnabled ? 1 : 0);
message.appendInt(this.danceId);
message.appendInt(this.userSource);
message.appendInt(this.quantifier);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(0);
message.appendInt(0);
}
@Override
public boolean saveData(WiredSettings settings) {
int[] intParams = settings.getIntParams();
this.resetSettings();
if (intParams.length > 0) this.selectedAction = normalizeAction(intParams[0]);
if (intParams.length > 1) this.signFilterEnabled = (intParams[1] == 1);
if (intParams.length > 2) this.signId = normalizeSignId(intParams[2]);
if (intParams.length > 3) this.danceFilterEnabled = (intParams[3] == 1);
if (intParams.length > 4) this.danceId = normalizeDanceId(intParams[4]);
if (intParams.length > 5) this.userSource = this.normalizeUserSource(intParams[5]);
if (intParams.length > 6) this.quantifier = normalizeQuantifier(intParams[6]);
return true;
}
protected void resetSettings() {
this.selectedAction = DEFAULT_ACTION;
this.signFilterEnabled = false;
this.signId = 0;
this.danceFilterEnabled = false;
this.danceId = 1;
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
this.quantifier = QUANTIFIER_ALL;
}
protected int normalizeAction(int action) {
switch (action) {
case WiredUserActionType.WAVE:
case WiredUserActionType.BLOW_KISS:
case WiredUserActionType.LAUGH:
case WiredUserActionType.AWAKE:
case WiredUserActionType.RELAX:
case WiredUserActionType.SIT:
case WiredUserActionType.STAND:
case WiredUserActionType.LAY:
case WiredUserActionType.SIGN:
case WiredUserActionType.DANCE:
case WiredUserActionType.THUMB_UP:
return action;
default:
return DEFAULT_ACTION;
}
}
protected int normalizeQuantifier(int value) {
return (value == QUANTIFIER_ANY) ? QUANTIFIER_ANY : QUANTIFIER_ALL;
}
protected int normalizeUserSource(int value) {
switch (value) {
case WiredSourceUtil.SOURCE_SELECTOR:
case WiredSourceUtil.SOURCE_SIGNAL:
case WiredSourceUtil.SOURCE_TRIGGER:
return value;
default:
return WiredSourceUtil.SOURCE_TRIGGER;
}
}
protected int normalizeSignId(int value) {
return (value < 0 || value > 17) ? 0 : value;
}
protected int normalizeDanceId(int value) {
return (value < 1 || value > 4) ? 1 : value;
}
protected boolean matchesAction(WiredContext ctx, RoomUnit roomUnit) {
if (roomUnit == null) {
return false;
}
if (this.matchesEventAction(ctx, roomUnit)) {
return true;
}
if (this.matchesCurrentState(roomUnit)) {
return true;
}
return this.matchesRecentAction(roomUnit);
}
protected boolean matchesEventAction(WiredContext ctx, RoomUnit roomUnit) {
RoomUnit actor = ctx.actor().orElse(null);
if (actor == null || actor.getId() != roomUnit.getId()) {
return false;
}
if (ctx.eventType() != com.eu.habbo.habbohotel.wired.core.WiredEvent.Type.USER_PERFORMS_ACTION) {
return false;
}
return this.matchesConfiguredAction(ctx.event().getActionId(), ctx.event().getActionParameter());
}
protected boolean matchesCurrentState(RoomUnit roomUnit) {
switch (this.selectedAction) {
case WiredUserActionType.SIT:
return roomUnit.hasStatus(RoomUnitStatus.SIT);
case WiredUserActionType.LAY:
return roomUnit.hasStatus(RoomUnitStatus.LAY);
case WiredUserActionType.RELAX:
return roomUnit.isIdle();
case WiredUserActionType.SIGN:
return this.matchesSignState(roomUnit);
case WiredUserActionType.DANCE:
return this.matchesDanceState(roomUnit);
default:
return false;
}
}
protected boolean matchesRecentAction(RoomUnit roomUnit) {
Object actionValue = roomUnit.getCacheable().get(CACHE_LAST_ACTION_ID);
Object parameterValue = roomUnit.getCacheable().get(CACHE_LAST_ACTION_PARAMETER);
Object timestampValue = roomUnit.getCacheable().get(CACHE_LAST_ACTION_TIMESTAMP);
if (!(actionValue instanceof Integer) || !(timestampValue instanceof Long)) {
return false;
}
long timestamp = (Long) timestampValue;
if ((System.currentTimeMillis() - timestamp) > TRANSIENT_ACTION_WINDOW_MS) {
return false;
}
int actionId = (Integer) actionValue;
int parameter = (parameterValue instanceof Integer) ? (Integer) parameterValue : -1;
return this.matchesConfiguredAction(actionId, parameter);
}
protected boolean matchesConfiguredAction(int actionId, int actionParameter) {
if (actionId != this.selectedAction) {
return false;
}
if (this.selectedAction == WiredUserActionType.SIGN && this.signFilterEnabled) {
return actionParameter == this.signId;
}
if (this.selectedAction == WiredUserActionType.DANCE && this.danceFilterEnabled) {
return actionParameter == this.danceId;
}
return true;
}
protected boolean matchesSignState(RoomUnit roomUnit) {
String signStatus = roomUnit.getStatus(RoomUnitStatus.SIGN);
if (signStatus == null) {
return false;
}
if (!this.signFilterEnabled) {
return true;
}
try {
return Integer.parseInt(signStatus) == this.signId;
} catch (NumberFormatException ignored) {
return false;
}
}
protected boolean matchesDanceState(RoomUnit roomUnit) {
int currentDance = roomUnit.getDanceType().getType();
if (currentDance <= 0) {
return false;
}
if (!this.danceFilterEnabled) {
return true;
}
return currentDance == this.danceId;
}
protected int getUserSource() {
return this.userSource;
}
protected int getQuantifier() {
return this.quantifier;
}
static class JsonData {
int selectedAction;
boolean signFilterEnabled;
int signId;
boolean danceFilterEnabled;
int danceId;
int userSource;
int quantifier;
public JsonData(int selectedAction, boolean signFilterEnabled, int signId, boolean danceFilterEnabled, int danceId, int userSource, int quantifier) {
this.selectedAction = selectedAction;
this.signFilterEnabled = signFilterEnabled;
this.signId = signId;
this.danceFilterEnabled = danceFilterEnabled;
this.danceId = danceId;
this.userSource = userSource;
this.quantifier = quantifier;
}
}
}
@@ -0,0 +1,175 @@
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
import com.eu.habbo.Emulator;
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.WiredSettings;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.wired.WiredEffectType;
import com.eu.habbo.habbohotel.wired.core.WiredContext;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
import gnu.trove.procedure.TObjectProcedure;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class WiredEffectFreeze extends InteractionWiredEffect {
private static final Set<Integer> ALLOWED_EFFECT_IDS = Set.of(218, 12, 11, 53, 163);
public static final WiredEffectType type = WiredEffectType.FREEZE;
private int effectId = 218;
private boolean cancelOnTeleport = false;
private int userSource = WiredSourceUtil.SOURCE_TRIGGER;
public WiredEffectFreeze(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredEffectFreeze(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public void execute(WiredContext ctx) {
Room room = ctx.room();
for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.userSource)) {
if (room.getHabbo(roomUnit) == null) {
continue;
}
WiredFreezeUtil.freeze(room, roomUnit, this.effectId, this.cancelOnTeleport);
}
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(this.effectId, this.cancelOnTeleport, this.getDelay(), this.userSource));
}
@Override
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.effectId = ALLOWED_EFFECT_IDS.contains(data.effectId) ? data.effectId : 218;
this.cancelOnTeleport = data.cancelOnTeleport;
this.setDelay(data.delay);
this.userSource = data.userSource;
} else {
this.effectId = 218;
this.cancelOnTeleport = false;
this.setDelay(0);
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
}
@Override
public void onPickUp() {
this.effectId = 218;
this.cancelOnTeleport = false;
this.setDelay(0);
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
@Override
public WiredEffectType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(false);
message.appendInt(5);
message.appendInt(0);
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(3);
message.appendInt(this.effectId);
message.appendInt(this.cancelOnTeleport ? 1 : 0);
message.appendInt(this.userSource);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(this.getDelay());
if (this.requiresTriggeringUser()) {
List<Integer> invalidTriggers = new ArrayList<>();
room.getRoomSpecialTypes().getTriggers(this.getX(), this.getY()).forEach(new TObjectProcedure<InteractionWiredTrigger>() {
@Override
public boolean execute(InteractionWiredTrigger object) {
if (!object.isTriggeredByRoomUnit()) {
invalidTriggers.add(object.getBaseItem().getSpriteId());
}
return true;
}
});
message.appendInt(invalidTriggers.size());
for (Integer i : invalidTriggers) {
message.appendInt(i);
}
} else {
message.appendInt(0);
}
}
@Override
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
if (settings.getIntParams().length < 3) {
throw new WiredSaveException("Invalid data");
}
int nextEffectId = settings.getIntParams()[0];
if (!ALLOWED_EFFECT_IDS.contains(nextEffectId)) {
throw new WiredSaveException("Invalid freeze effect");
}
int delay = settings.getDelay();
if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) {
throw new WiredSaveException("Delay too long");
}
this.effectId = nextEffectId;
this.cancelOnTeleport = settings.getIntParams()[1] == 1;
this.userSource = settings.getIntParams()[2];
this.setDelay(delay);
return true;
}
@Override
public boolean requiresTriggeringUser() {
return this.userSource == WiredSourceUtil.SOURCE_TRIGGER;
}
static class JsonData {
int effectId;
boolean cancelOnTeleport;
int delay;
int userSource;
public JsonData(int effectId, boolean cancelOnTeleport, int delay, int userSource) {
this.effectId = effectId;
this.cancelOnTeleport = cancelOnTeleport;
this.delay = delay;
this.userSource = userSource;
}
}
}
@@ -0,0 +1,354 @@
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
import com.eu.habbo.Emulator;
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.wired.WiredSettings;
import com.eu.habbo.habbohotel.rooms.FurnitureMovementError;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomTile;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredEffectType;
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;
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class WiredEffectFurniToFurni extends InteractionWiredEffect {
private static final int SOURCE_SECONDARY_SELECTED = 101;
private static final String FURNI_SPLIT_REGEX = "[;,\\t]";
private static final String FURNI_DELIMITER = ";";
public static final WiredEffectType type = WiredEffectType.FURNI_TO_FURNI;
private final List<HabboItem> moveItems = new ArrayList<>();
private final List<HabboItem> targetItems = new ArrayList<>();
private int moveSource = WiredSourceUtil.SOURCE_TRIGGER;
private int targetSource = WiredSourceUtil.SOURCE_TRIGGER;
public WiredEffectFurniToFurni(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredEffectFurniToFurni(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public void execute(WiredContext ctx) {
Room room = ctx.room();
if (room == null) {
return;
}
HabboItem moveItem = this.resolveLastMoveItem(ctx);
HabboItem targetItem = this.resolveLastTargetItem(ctx);
if (moveItem == null || targetItem == null || moveItem.getId() == targetItem.getId()) {
return;
}
RoomTile targetTile = room.getLayout().getTile(targetItem.getX(), targetItem.getY());
if (targetTile == null) {
return;
}
FurnitureMovementError error = room.moveFurniTo(moveItem, targetTile, moveItem.getRotation(), null, true, false);
if (error != FurnitureMovementError.NONE) {
room.moveFurniTo(moveItem, targetTile, moveItem.getRotation(), targetItem.getZ(), null, true, false);
}
}
@Deprecated
@Override
public boolean execute(com.eu.habbo.habbohotel.rooms.RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.getDelay(),
this.moveItems.stream().map(HabboItem::getId).collect(Collectors.toList()),
this.targetItems.stream().map(HabboItem::getId).collect(Collectors.toList()),
this.moveSource,
this.targetSource
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.moveItems.clear();
this.targetItems.clear();
String wiredData = set.getString("wired_data");
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.setDelay(data.delay);
this.moveSource = data.moveSource;
this.targetSource = this.normalizeTargetSource(data.targetSource);
this.loadItems(room, data.itemIds, this.moveItems);
this.loadItems(room, data.targetItemIds, this.targetItems);
if (this.moveSource == WiredSourceUtil.SOURCE_TRIGGER && !this.moveItems.isEmpty()) {
this.moveSource = WiredSourceUtil.SOURCE_SELECTED;
}
if (this.targetSource == WiredSourceUtil.SOURCE_TRIGGER && !this.targetItems.isEmpty()) {
this.targetSource = SOURCE_SECONDARY_SELECTED;
}
return;
}
if (wiredData != null && !wiredData.isEmpty()) {
String[] wiredDataOld = wiredData.split("\t");
if (wiredDataOld.length >= 1) {
this.setDelay(Integer.parseInt(wiredDataOld[0]));
}
if (wiredDataOld.length >= 2 && !wiredDataOld[1].trim().isEmpty()) {
this.loadItems(room, this.parseIds(wiredDataOld[1], room), this.moveItems);
}
}
this.moveSource = this.moveItems.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED;
this.targetSource = this.targetItems.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : SOURCE_SECONDARY_SELECTED;
}
@Override
public void onPickUp() {
this.moveItems.clear();
this.targetItems.clear();
this.moveSource = WiredSourceUtil.SOURCE_TRIGGER;
this.targetSource = WiredSourceUtil.SOURCE_TRIGGER;
this.setDelay(0);
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
this.validateItems(this.moveItems);
this.validateItems(this.targetItems);
message.appendBoolean(false);
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
message.appendInt(this.moveItems.size());
for (HabboItem item : this.moveItems) {
message.appendInt(item.getId());
}
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString(this.serializeIds(this.targetItems));
message.appendInt(2);
message.appendInt(this.moveSource);
message.appendInt(this.targetSource);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(this.getDelay());
message.appendInt(0);
}
@Override
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
this.moveSource = (settings.getIntParams().length > 0) ? settings.getIntParams()[0] : WiredSourceUtil.SOURCE_TRIGGER;
this.targetSource = this.normalizeTargetSource((settings.getIntParams().length > 1) ? settings.getIntParams()[1] : WiredSourceUtil.SOURCE_TRIGGER);
Room room = this.getRoom();
if (room == null) {
throw new WiredSaveException("Room not found");
}
List<HabboItem> newMoveItems = new ArrayList<>();
if (this.moveSource == WiredSourceUtil.SOURCE_SELECTED) {
for (int itemId : settings.getFurniIds()) {
HabboItem item = room.getHabboItem(itemId);
if (item == null) {
throw new WiredSaveException(String.format("Item %s not found", itemId));
}
newMoveItems.add(item);
}
}
if (newMoveItems.size() > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
throw new WiredSaveException("Too many furni selected");
}
List<HabboItem> newTargetItems = new ArrayList<>();
if (this.targetSource == SOURCE_SECONDARY_SELECTED) {
newTargetItems.addAll(this.parseItems(settings.getStringParam(), room));
}
if (newTargetItems.size() > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
throw new WiredSaveException("Too many furni selected");
}
int delay = settings.getDelay();
if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) {
throw new WiredSaveException("Delay too long");
}
this.moveItems.clear();
this.moveItems.addAll(newMoveItems);
this.targetItems.clear();
this.targetItems.addAll(newTargetItems);
this.setDelay(delay);
return true;
}
@Override
public WiredEffectType getType() {
return type;
}
@Override
protected long requiredCooldown() {
return COOLDOWN_MOVEMENT;
}
private HabboItem resolveLastMoveItem(WiredContext ctx) {
return this.resolveLastItem(ctx, this.moveSource, this.moveItems);
}
private HabboItem resolveLastTargetItem(WiredContext ctx) {
int source = (this.targetSource == SOURCE_SECONDARY_SELECTED) ? WiredSourceUtil.SOURCE_SELECTED : this.targetSource;
return this.resolveLastItem(ctx, source, this.targetItems);
}
private HabboItem resolveLastItem(WiredContext ctx, int source, List<HabboItem> items) {
if (source == WiredSourceUtil.SOURCE_SELECTED) {
this.validateItems(items);
}
List<HabboItem> resolvedItems = WiredSourceUtil.resolveItems(ctx, source, items);
if (resolvedItems.isEmpty()) {
return null;
}
for (int index = resolvedItems.size() - 1; index >= 0; index--) {
HabboItem item = resolvedItems.get(index);
if (item != null) {
return item;
}
}
return null;
}
private List<HabboItem> parseItems(String data, Room room) throws WiredSaveException {
List<HabboItem> items = new ArrayList<>();
if (data == null || data.trim().isEmpty() || room == null) {
return items;
}
Set<Integer> seen = new HashSet<>();
for (String part : data.split(FURNI_SPLIT_REGEX)) {
if (part == null) {
continue;
}
String trimmed = part.trim();
if (trimmed.isEmpty()) {
continue;
}
int itemId;
try {
itemId = Integer.parseInt(trimmed);
} catch (NumberFormatException e) {
continue;
}
if (itemId <= 0 || !seen.add(itemId)) {
continue;
}
HabboItem item = room.getHabboItem(itemId);
if (item == null) {
throw new WiredSaveException(String.format("Item %s not found", itemId));
}
items.add(item);
}
return items;
}
private List<Integer> parseIds(String data, Room room) {
try {
return this.parseItems(data, room).stream().map(HabboItem::getId).collect(Collectors.toList());
} catch (WiredSaveException e) {
return new ArrayList<>();
}
}
private void loadItems(Room room, List<Integer> itemIds, List<HabboItem> target) {
if (room == null || itemIds == null || itemIds.isEmpty()) {
return;
}
for (Integer itemId : itemIds) {
HabboItem item = room.getHabboItem(itemId);
if (item != null) {
target.add(item);
}
}
}
private int normalizeTargetSource(int source) {
return (source == WiredSourceUtil.SOURCE_SELECTED) ? SOURCE_SECONDARY_SELECTED : source;
}
private String serializeIds(List<HabboItem> items) {
if (items == null || items.isEmpty()) {
return "";
}
return items.stream()
.map(HabboItem::getId)
.distinct()
.map(String::valueOf)
.collect(Collectors.joining(FURNI_DELIMITER));
}
static class JsonData {
int delay;
List<Integer> itemIds;
List<Integer> targetItemIds;
int moveSource;
int targetSource;
public JsonData(int delay, List<Integer> itemIds, List<Integer> targetItemIds, int moveSource, int targetSource) {
this.delay = delay;
this.itemIds = itemIds;
this.targetItemIds = targetItemIds;
this.moveSource = moveSource;
this.targetSource = targetSource;
}
}
}
@@ -0,0 +1,57 @@
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.rooms.FurnitureMovementError;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomTile;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredEffectType;
import com.eu.habbo.habbohotel.wired.core.WiredContext;
import java.sql.ResultSet;
import java.sql.SQLException;
public class WiredEffectFurniToUser extends WiredEffectUserFurniBase {
public static final WiredEffectType type = WiredEffectType.FURNI_TO_USER;
public WiredEffectFurniToUser(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredEffectFurniToUser(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public void execute(WiredContext ctx) {
Room room = ctx.room();
HabboItem item = this.resolveLastItem(ctx);
Habbo habbo = this.resolveLastHabbo(room, ctx);
if (room == null || item == null || habbo == null || habbo.getRoomUnit() == null) {
return;
}
RoomTile targetTile = habbo.getRoomUnit().getCurrentLocation();
if (targetTile == null) {
return;
}
FurnitureMovementError error = room.moveFurniTo(item, targetTile, item.getRotation(), null, true, false);
if (error != FurnitureMovementError.NONE && item.getBaseItem().getStateCount() > 0) {
room.moveFurniTo(item, targetTile, item.getRotation(), item.getZ(), null, true, false);
}
}
@Deprecated
@Override
public boolean execute(com.eu.habbo.habbohotel.rooms.RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public WiredEffectType getType() {
return type;
}
}
@@ -140,15 +140,6 @@ public class WiredEffectGiveReward extends InteractionWiredEffect {
return type;
}
@Override
public void onClick(GameClient client, Room room, Object[] objects) throws Exception {
super.onClick(client, room, objects);
if (client.getHabbo().hasPermission(Permission.ACC_SUPERWIRED)) {
client.getHabbo().whisper(Emulator.getTexts().getValue("hotel.wired.superwired.info"), RoomChatMessageBubbles.BOT);
}
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(false);
@@ -50,6 +50,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int
@Override
public void execute(WiredContext ctx) {
Room room = ctx.room();
this.refresh();
if(this.settings.isEmpty())
return;
@@ -57,33 +58,63 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int
if (room.getLayout() == null)
return;
java.util.Set<Integer> allowedItemIds = null;
if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) {
allowedItemIds = new java.util.HashSet<>();
for (HabboItem si : WiredSourceUtil.resolveItems(ctx, this.furniSource, null)) {
if (si != null) {
allowedItemIds.add(si.getId());
}
}
if (allowedItemIds.isEmpty()) {
return;
}
}
if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
for (WiredMatchFurniSetting setting : this.settings) {
if (allowedItemIds != null && !allowedItemIds.contains(setting.item_id)) continue;
HabboItem item = room.getHabboItem(setting.item_id);
if (item != null) {
this.applySetting(room, item, setting);
}
}
return;
}
List<HabboItem> targets = WiredSourceUtil.resolveItems(ctx, this.furniSource, null);
if (targets.isEmpty()) {
return;
}
for (HabboItem item : targets) {
if (item == null) continue;
WiredMatchFurniSetting setting = this.resolveSettingForTarget(room, item);
if (setting == null) continue;
this.applySetting(room, item, setting);
}
}
private WiredMatchFurniSetting resolveSettingForTarget(Room room, HabboItem target) {
WiredMatchFurniSetting fallback = null;
for (WiredMatchFurniSetting setting : this.settings) {
HabboItem sourceItem = room.getHabboItem(setting.item_id);
if (sourceItem == null) continue;
if (sourceItem.getBaseItem().getId() != target.getBaseItem().getId()) continue;
if (setting.state.equals(target.getExtradata())) {
return setting;
}
if (fallback == null) {
fallback = setting;
}
}
return fallback;
}
private void applySetting(Room room, HabboItem item, WiredMatchFurniSetting setting) {
if (this.state && (this.checkForWiredResetPermission && item.allowWiredResetState())) {
if (!setting.state.equals(" ") && !item.getExtradata().equals(setting.state)) {
item.setExtradata(setting.state);
item.needsUpdate(true);
room.updateItemState(item);
}
}
RoomTile oldLocation = room.getLayout().getTile(item.getX(), item.getY());
if (oldLocation == null) continue;
if (oldLocation == null) return;
double oldZ = item.getZ();
if(this.direction && !this.position) {
@@ -104,9 +135,6 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int
}
}
}
}
}
}
@Deprecated
@@ -134,9 +162,6 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int
this.settings.clear();
this.settings.addAll(data.items);
this.furniSource = data.furniSource;
if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.settings.isEmpty()) {
this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
}
}
else {
String[] data = set.getString("wired_data").split(":");
@@ -221,14 +246,14 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int
if (room == null)
throw new WiredSaveException("Trying to save wired in unloaded room");
List<WiredMatchFurniSetting> newSettings = new ArrayList<>();
if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
int itemsCount = settings.getFurniIds().length;
if(itemsCount > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
throw new WiredSaveException("Too many furni selected");
}
List<WiredMatchFurniSetting> newSettings = new ArrayList<>();
for (int i = 0; i < itemsCount; i++) {
int itemId = settings.getFurniIds()[i];
HabboItem it = room.getHabboItem(itemId);
@@ -238,7 +263,6 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int
newSettings.add(new WiredMatchFurniSetting(it.getId(), this.checkForWiredResetPermission && it.allowWiredResetState() ? it.getExtradata() : " ", it.getRotation(), it.getX(), it.getY()));
}
}
int delay = settings.getDelay();
@@ -249,9 +273,7 @@ public class WiredEffectMatchFurni extends InteractionWiredEffect implements Int
this.direction = setDirection;
this.position = setPosition;
this.settings.clear();
if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
this.settings.addAll(newSettings);
}
this.setDelay(delay);
return true;
@@ -76,7 +76,7 @@ public class WiredEffectMuteHabbo extends InteractionWiredEffect {
if (room.hasRights(habbo)) continue;
room.muteHabbo(habbo, 60);
room.muteHabbo(habbo, Math.max(1, this.length));
habbo.getClient().sendResponse(new RoomUserWhisperComposer(new RoomChatMessage(this.message.replace("%user%", habbo.getHabboInfo().getUsername()).replace("%online_count%", Emulator.getGameEnvironment().getHabboManager().getOnlineCount() + "").replace("%room_count%", Emulator.getGameEnvironment().getRoomManager().getActiveRooms().size() + ""), habbo, habbo, RoomChatMessageBubbles.WIRED)));
}
@@ -0,0 +1,283 @@
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
import com.eu.habbo.Emulator;
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.wired.WiredSettings;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomTile;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredEffectType;
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;
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class WiredEffectRelativeMove extends InteractionWiredEffect {
private static final int HORIZONTAL_NEGATIVE = 0;
private static final int HORIZONTAL_POSITIVE = 1;
private static final int VERTICAL_NEGATIVE = 0;
private static final int VERTICAL_POSITIVE = 1;
private static final int MAX_DISTANCE = 20;
public static final WiredEffectType type = WiredEffectType.RELATIVE_MOVE;
private final List<HabboItem> items = new ArrayList<>();
private int horizontalDirection = HORIZONTAL_POSITIVE;
private int horizontalDistance = 0;
private int verticalDirection = VERTICAL_POSITIVE;
private int verticalDistance = 0;
private int furniSource = WiredSourceUtil.SOURCE_TRIGGER;
public WiredEffectRelativeMove(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredEffectRelativeMove(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public void execute(WiredContext ctx) {
Room room = ctx.room();
if (room == null || room.getLayout() == null) {
return;
}
List<HabboItem> effectiveItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items);
if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
this.items.removeIf(item -> item == null
|| item.getRoomId() != this.getRoomId()
|| room.getHabboItem(item.getId()) == null);
}
int deltaX = this.getHorizontalOffset();
int deltaY = this.getVerticalOffset();
if (deltaX == 0 && deltaY == 0) {
return;
}
for (HabboItem item : effectiveItems) {
if (item == null || item.getRoomId() != this.getRoomId()) {
continue;
}
short targetX = (short) (item.getX() + deltaX);
short targetY = (short) (item.getY() + deltaY);
RoomTile targetTile = room.getLayout().getTile(targetX, targetY);
if (targetTile == null) {
continue;
}
room.moveFurniTo(item, targetTile, item.getRotation(), null, true, false);
}
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.getDelay(),
this.items.stream().map(HabboItem::getId).collect(Collectors.toList()),
this.horizontalDirection,
this.horizontalDistance,
this.verticalDirection,
this.verticalDistance,
this.furniSource
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.items.clear();
String wiredData = set.getString("wired_data");
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.setDelay(data.delay);
this.horizontalDirection = this.normalizeBinary(data.horizontalDirection, HORIZONTAL_POSITIVE);
this.horizontalDistance = this.normalizeDistance(data.horizontalDistance);
this.verticalDirection = this.normalizeBinary(data.verticalDirection, VERTICAL_POSITIVE);
this.verticalDistance = this.normalizeDistance(data.verticalDistance);
this.furniSource = data.furniSource;
if (data.itemIds != null) {
for (Integer id : data.itemIds) {
HabboItem item = room.getHabboItem(id);
if (item != null) {
this.items.add(item);
}
}
}
return;
}
this.horizontalDirection = HORIZONTAL_POSITIVE;
this.horizontalDistance = 0;
this.verticalDirection = VERTICAL_POSITIVE;
this.verticalDistance = 0;
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
this.setDelay(0);
}
@Override
public void onPickUp() {
this.items.clear();
this.horizontalDirection = HORIZONTAL_POSITIVE;
this.horizontalDistance = 0;
this.verticalDirection = VERTICAL_POSITIVE;
this.verticalDistance = 0;
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
this.setDelay(0);
}
@Override
public WiredEffectType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
List<HabboItem> itemsSnapshot = new ArrayList<>(this.items);
itemsSnapshot.removeIf(item -> item == null
|| item.getRoomId() != this.getRoomId()
|| room.getHabboItem(item.getId()) == null);
this.items.clear();
this.items.addAll(itemsSnapshot);
message.appendBoolean(false);
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
message.appendInt(itemsSnapshot.size());
for (HabboItem item : itemsSnapshot) {
message.appendInt(item.getId());
}
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(5);
message.appendInt(this.horizontalDirection);
message.appendInt(this.horizontalDistance);
message.appendInt(this.verticalDirection);
message.appendInt(this.verticalDistance);
message.appendInt(this.furniSource);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(this.getDelay());
message.appendInt(0);
}
@Override
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
int[] params = settings.getIntParams();
if (params.length < 5) {
throw new WiredSaveException("Invalid data");
}
this.horizontalDirection = this.normalizeBinary(params[0], HORIZONTAL_POSITIVE);
this.horizontalDistance = this.normalizeDistance(params[1]);
this.verticalDirection = this.normalizeBinary(params[2], VERTICAL_POSITIVE);
this.verticalDistance = this.normalizeDistance(params[3]);
this.furniSource = params[4];
int delay = settings.getDelay();
if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) {
throw new WiredSaveException("Delay too long");
}
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
if (room == null) {
throw new WiredSaveException("Room not found");
}
if (settings.getFurniIds().length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
throw new WiredSaveException("Too many furni selected");
}
List<HabboItem> newItems = new ArrayList<>();
for (int itemId : settings.getFurniIds()) {
HabboItem item = room.getHabboItem(itemId);
if (item == null) {
throw new WiredSaveException(String.format("Item %s not found", itemId));
}
newItems.add(item);
}
this.items.clear();
this.items.addAll(newItems);
this.setDelay(delay);
return true;
}
private int getHorizontalOffset() {
if (this.horizontalDistance <= 0) {
return 0;
}
return (this.horizontalDirection == HORIZONTAL_NEGATIVE) ? -this.horizontalDistance : this.horizontalDistance;
}
private int getVerticalOffset() {
if (this.verticalDistance <= 0) {
return 0;
}
return (this.verticalDirection == VERTICAL_NEGATIVE) ? -this.verticalDistance : this.verticalDistance;
}
private int normalizeBinary(int value, int fallback) {
if (value == 0 || value == 1) {
return value;
}
return fallback;
}
private int normalizeDistance(int value) {
return Math.max(0, Math.min(MAX_DISTANCE, value));
}
static class JsonData {
int delay;
List<Integer> itemIds;
int horizontalDirection;
int horizontalDistance;
int verticalDirection;
int verticalDistance;
int furniSource;
public JsonData(int delay, List<Integer> itemIds, int horizontalDirection, int horizontalDistance, int verticalDirection, int verticalDistance, int furniSource) {
this.delay = delay;
this.itemIds = itemIds;
this.horizontalDirection = horizontalDirection;
this.horizontalDistance = horizontalDistance;
this.verticalDirection = verticalDirection;
this.verticalDistance = verticalDistance;
this.furniSource = furniSource;
}
}
}
@@ -24,6 +24,7 @@ import org.slf4j.LoggerFactory;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@@ -36,14 +37,16 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
private static final int ANTENNA_PICKED = 0;
private static final int ANTENNA_TRIGGER = 1;
private static final int FORWARD_NONE = 0;
private static final int FORWARD_TRIGGER = 1;
private static final String ANTENNA_INTERACTION = "antenna";
private static final String FORWARD_ITEM_SPLIT_REGEX = "[;,\\t]";
private static final long ANTENNA_PULSE_MS = 300L;
private static final ConcurrentHashMap<Integer, Long> ANTENNA_PULSE_TOKENS = new ConcurrentHashMap<>();
private THashSet<HabboItem> items;
private THashSet<HabboItem> forwardItems;
private int antennaSource = ANTENNA_PICKED;
private int furniForward = FORWARD_NONE;
private int userForward = FORWARD_NONE;
private int furniForward = WiredSourceUtil.SOURCE_TRIGGER;
private int userForward = WiredSourceUtil.SOURCE_TRIGGER;
private boolean signalPerFurni = false;
private boolean signalPerUser = false;
private int channel = 0;
@@ -51,11 +54,13 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
public WiredEffectSendSignal(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
this.items = new THashSet<>();
this.forwardItems = new THashSet<>();
}
public WiredEffectSendSignal(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
this.items = new THashSet<>();
this.forwardItems = new THashSet<>();
}
@Override
@@ -77,73 +82,68 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
.map(Collections::<HabboItem>singleton)
.orElse(Collections.emptySet());
} else {
antennas = ctx.targets().isItemsModifiedBySelector()
? new ArrayList<>(ctx.targets().items())
: new ArrayList<>(this.items);
Collection<HabboItem> baseAntennas = new ArrayList<>(this.items);
if (baseAntennas.isEmpty() && antennaSource > ANTENNA_TRIGGER) {
HabboItem antenna = room.getHabboItem(antennaSource);
antennas = (antenna != null) ? Collections.singleton(antenna) : Collections.emptySet();
} else {
antennas = baseAntennas;
}
}
if (antennas.isEmpty()) {
List<HabboItem> resolvedAntennas = antennas.stream()
.filter(Objects::nonNull)
.filter(this::isAntennaItem)
.collect(Collectors.toList());
if (resolvedAntennas.isEmpty()) {
LOGGER.debug("[SendSignal] No antennas resolved, aborting. antennaSource={}, selectorModified={}", antennaSource, ctx.targets().isItemsModifiedBySelector());
return;
}
LOGGER.debug("[SendSignal] Resolved {} antenna(s), firing signals", antennas.size());
LOGGER.debug("[SendSignal] Resolved {} antenna(s), firing signals", resolvedAntennas.size());
RoomUnit forwardedUser = null;
if (userForward == FORWARD_TRIGGER) {
forwardedUser = ctx.actor().orElse(null);
}
List<RoomUnit> forwardedUsers = WiredSourceUtil.resolveUsers(ctx, this.userForward);
List<HabboItem> forwardedFurni = WiredSourceUtil.resolveItems(ctx, this.furniForward, this.forwardItems);
HabboItem forwardedFurni = null;
if (furniForward == FORWARD_TRIGGER) {
forwardedFurni = ctx.sourceItem().orElse(null);
}
RoomUnit defaultUser = forwardedUsers.isEmpty() ? null : forwardedUsers.get(0);
HabboItem defaultFurni = forwardedFurni.isEmpty() ? null : forwardedFurni.get(0);
Set<String> visitedTiles = new HashSet<>();
List<RoomTile> antennaTiles = new ArrayList<>();
for (HabboItem antenna : antennas) {
if (antenna == null) continue;
String key = antenna.getX() + "," + antenna.getY();
if (visitedTiles.add(key)) {
RoomTile tile = room.getLayout().getTile(antenna.getX(), antenna.getY());
if (tile != null) {
antennaTiles.add(tile);
}
}
}
Collection<RoomUnit> usersToSend = (signalPerUser && !forwardedUsers.isEmpty())
? forwardedUsers
: Collections.singletonList(defaultUser);
Collection<HabboItem> furniToSend = (signalPerFurni && !forwardedFurni.isEmpty())
? forwardedFurni
: Collections.singletonList(defaultFurni);
int nextDepth = currentDepth + 1;
if (signalPerFurni || signalPerUser) {
if (signalPerFurni) {
for (RoomTile tile : antennaTiles) {
fireSignalAtTile(room, tile, forwardedUser, forwardedFurni, nextDepth);
for (RoomUnit user : usersToSend) {
for (HabboItem sourceItem : furniToSend) {
for (HabboItem antenna : resolvedAntennas) {
fireSignalAtAntenna(room, antenna, user, sourceItem, nextDepth);
}
}
if (signalPerUser && ctx.targets().hasUsers()) {
for (RoomUnit user : ctx.targets().users()) {
for (RoomTile tile : antennaTiles) {
fireSignalAtTile(room, tile, user, forwardedFurni, nextDepth);
}
}
} else if (!signalPerFurni) {
for (RoomTile tile : antennaTiles) {
fireSignalAtTile(room, tile, forwardedUser, forwardedFurni, nextDepth);
}
}
} else {
for (RoomTile tile : antennaTiles) {
fireSignalAtTile(room, tile, forwardedUser, forwardedFurni, nextDepth);
}
}
}
private void fireSignalAtTile(Room room, RoomTile tile, RoomUnit actor, HabboItem sourceItem, int depth) {
LOGGER.debug("[SendSignal] fireSignalAtTile: tile={},{} depth={} channel={} actor={} sourceItem={}", tile.x, tile.y, depth, channel, actor != null ? actor.getId() : "null", sourceItem != null ? sourceItem.getId() : "null");
private void fireSignalAtAntenna(Room room, HabboItem antenna, RoomUnit actor, HabboItem sourceItem, int depth) {
if (antenna == null) return;
RoomTile tile = room.getLayout().getTile(antenna.getX(), antenna.getY());
if (tile == null) return;
pulseAntenna(room, antenna);
int signalChannel = antenna.getId();
LOGGER.debug("[SendSignal] fireSignalAtAntenna: antennaId={} tile={},{} depth={} channel={} actor={} sourceItem={}",
signalChannel, tile.x, tile.y, depth, signalChannel, actor != null ? actor.getId() : "null", sourceItem != null ? sourceItem.getId() : "null");
WiredEvent.Builder builder = WiredEvent.builder(WiredEvent.Type.SIGNAL_RECEIVED, room)
.tile(tile)
.callStackDepth(depth)
.signalChannel(this.channel)
.signalChannel(signalChannel)
.triggeredByEffect(true);
if (actor != null) builder.actor(actor);
@@ -153,6 +153,33 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
LOGGER.debug("[SendSignal] handleEvent returned: {}", result);
}
private void pulseAntenna(Room room, HabboItem antenna) {
if (room == null || antenna == null || antenna.getBaseItem() == null) return;
if (antenna.getBaseItem().getStateCount() <= 1) return;
final long token = System.currentTimeMillis();
ANTENNA_PULSE_TOKENS.put(antenna.getId(), token);
if ("1".equals(antenna.getExtradata())) {
antenna.setExtradata("0");
room.updateItemState(antenna);
}
antenna.setExtradata("1");
room.updateItemState(antenna);
Emulator.getThreading().run(() -> {
if (!room.isLoaded()) return;
Long currentToken = ANTENNA_PULSE_TOKENS.get(antenna.getId());
if (currentToken == null || currentToken.longValue() != token) return;
antenna.setExtradata("0");
room.updateItemState(antenna);
ANTENNA_PULSE_TOKENS.remove(antenna.getId(), token);
}, ANTENNA_PULSE_MS);
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
List<HabboItem> itemsSnapshot = new ArrayList<>(this.items);
@@ -161,6 +188,16 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
item.getRoomId() != this.getRoomId() || room.getHabboItem(item.getId()) == null);
this.items.retainAll(itemsSnapshot);
List<HabboItem> forwardSnapshot = new ArrayList<>(this.forwardItems);
forwardSnapshot.removeIf(item ->
item.getRoomId() != this.getRoomId() || room.getHabboItem(item.getId()) == null);
this.forwardItems.retainAll(forwardSnapshot);
String forwardString = forwardSnapshot.stream()
.filter(Objects::nonNull)
.map(item -> Integer.toString(item.getId()))
.collect(Collectors.joining(";"));
message.appendBoolean(false);
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
message.appendInt(itemsSnapshot.size());
@@ -169,7 +206,7 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
}
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendString(forwardString);
message.appendInt(6);
message.appendInt(antennaSource);
@@ -219,6 +256,12 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
newItems.add(it);
}
for (HabboItem receiver : newItems) {
if (!isAntennaItem(receiver)) {
throw new WiredSaveException("Only antenna furni can be selected");
}
}
if (room != null && room.getRoomSpecialTypes() != null) {
for (HabboItem receiver : newItems) {
int count = room.getRoomSpecialTypes().countSendersTargetingReceiver(receiver.getId(), this);
@@ -234,18 +277,36 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
}
int[] params = settings.getIntParams();
this.antennaSource = params.length > 0 ? params[0] : ANTENNA_PICKED;
this.furniForward = params.length > 1 ? params[1] : FORWARD_NONE;
this.userForward = params.length > 2 ? params[2] : FORWARD_NONE;
int requestedAntennaSource = params.length > 0 ? params[0] : ANTENNA_PICKED;
this.furniForward = normalizeSource(params.length > 1 ? params[1] : WiredSourceUtil.SOURCE_TRIGGER);
this.userForward = normalizeSource(params.length > 2 ? params[2] : WiredSourceUtil.SOURCE_TRIGGER);
this.signalPerFurni = params.length > 3 && params[3] == 1;
this.signalPerUser = params.length > 4 && params[4] == 1;
this.channel = params.length > 5 ? params[5] : 0;
this.antennaSource = requestedAntennaSource;
if (!newItems.isEmpty()) {
this.antennaSource = newItems.get(0).getId();
}
List<HabboItem> newForwardItems = new ArrayList<>();
if (this.furniForward == WiredSourceUtil.SOURCE_SELECTED && room != null) {
newForwardItems = parseForwardItems(settings.getStringParam(), room);
}
if (newForwardItems.size() > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
throw new WiredSaveException("Too many furni selected");
}
this.items.clear();
this.items.addAll(newItems);
this.forwardItems.clear();
if (this.furniForward == WiredSourceUtil.SOURCE_SELECTED) {
this.forwardItems.addAll(newForwardItems);
}
this.setDelay(delay);
LOGGER.debug("[SendSignal] saveData: antennaSource={}, furniForward={}, userForward={}, signalPerFurni={}, signalPerUser={}, channel={}, items={}",
antennaSource, furniForward, userForward, signalPerFurni, signalPerUser, channel, items.size());
LOGGER.debug("[SendSignal] saveData: antennaSource={}, furniForward={}, userForward={}, signalPerFurni={}, signalPerUser={}, channel={}, items={}, forwardItems={}",
antennaSource, furniForward, userForward, signalPerFurni, signalPerUser, channel, items.size(), forwardItems.size());
return true;
}
@@ -259,9 +320,11 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
@Override
public String getWiredData() {
List<HabboItem> itemsSnapshot = new ArrayList<>(this.items);
List<HabboItem> forwardSnapshot = new ArrayList<>(this.forwardItems);
return WiredManager.getGson().toJson(new JsonData(
this.getDelay(),
itemsSnapshot.stream().map(HabboItem::getId).collect(Collectors.toList()),
forwardSnapshot.stream().map(HabboItem::getId).collect(Collectors.toList()),
antennaSource, furniForward, userForward, signalPerFurni, signalPerUser, channel
));
}
@@ -269,14 +332,15 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.items = new THashSet<>();
this.forwardItems = new THashSet<>();
String wiredData = set.getString("wired_data");
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.setDelay(data.delay);
this.antennaSource = data.antennaSource;
this.furniForward = data.furniForward;
this.userForward = data.userForward;
this.furniForward = normalizeSource(data.furniForward);
this.userForward = normalizeSource(data.userForward);
this.signalPerFurni = data.signalPerFurni;
this.signalPerUser = data.signalPerUser;
this.channel = data.channel;
@@ -286,21 +350,84 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
if (item != null) this.items.add(item);
}
}
if (data.forwardItemIds != null) {
for (Integer id : data.forwardItemIds) {
HabboItem item = room.getHabboItem(id);
if (item != null) this.forwardItems.add(item);
}
}
if (this.antennaSource <= ANTENNA_TRIGGER && !this.items.isEmpty()) {
HabboItem first = this.items.iterator().next();
if (first != null) this.antennaSource = first.getId();
}
}
}
@Override
public void onPickUp() {
this.items.clear();
this.forwardItems.clear();
this.antennaSource = ANTENNA_PICKED;
this.furniForward = FORWARD_NONE;
this.userForward = FORWARD_NONE;
this.furniForward = WiredSourceUtil.SOURCE_TRIGGER;
this.userForward = WiredSourceUtil.SOURCE_TRIGGER;
this.signalPerFurni = false;
this.signalPerUser = false;
this.channel = 0;
this.setDelay(0);
}
private int normalizeSource(int source) {
if (source == 1) return WiredSourceUtil.SOURCE_TRIGGER;
if (source == WiredSourceUtil.SOURCE_TRIGGER
|| source == WiredSourceUtil.SOURCE_SELECTED
|| source == WiredSourceUtil.SOURCE_SELECTOR
|| source == WiredSourceUtil.SOURCE_SIGNAL) {
return source;
}
return WiredSourceUtil.SOURCE_TRIGGER;
}
private List<HabboItem> parseForwardItems(String data, Room room) throws WiredSaveException {
List<HabboItem> results = new ArrayList<>();
if (data == null || data.trim().isEmpty() || room == null) return results;
Set<Integer> seen = new HashSet<>();
String[] parts = data.split(FORWARD_ITEM_SPLIT_REGEX);
for (String part : parts) {
if (part == null) continue;
String trimmed = part.trim();
if (trimmed.isEmpty()) continue;
int itemId;
try {
itemId = Integer.parseInt(trimmed);
} catch (NumberFormatException e) {
continue;
}
if (itemId <= 0 || !seen.add(itemId)) continue;
HabboItem item = room.getHabboItem(itemId);
if (item == null) throw new WiredSaveException(String.format("Item %s not found", itemId));
results.add(item);
}
return results;
}
private boolean isAntennaItem(HabboItem item) {
if (item == null || item.getBaseItem() == null || item.getBaseItem().getInteractionType() == null) return false;
String interaction = item.getBaseItem().getInteractionType().getName();
if (interaction == null) return false;
String normalized = interaction.toLowerCase();
return normalized.equals(ANTENNA_INTERACTION);
}
@Override
public WiredEffectType getType() {
return type;
@@ -328,6 +455,7 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
static class JsonData {
int delay;
List<Integer> itemIds;
List<Integer> forwardItemIds;
int antennaSource;
int furniForward;
int userForward;
@@ -335,10 +463,11 @@ public class WiredEffectSendSignal extends InteractionWiredEffect {
boolean signalPerUser;
int channel;
public JsonData(int delay, List<Integer> itemIds, int antennaSource, int furniForward,
public JsonData(int delay, List<Integer> itemIds, List<Integer> forwardItemIds, int antennaSource, int furniForward,
int userForward, boolean signalPerFurni, boolean signalPerUser, int channel) {
this.delay = delay;
this.itemIds = itemIds;
this.forwardItemIds = forwardItemIds;
this.antennaSource = antennaSource;
this.furniForward = furniForward;
this.userForward = userForward;
@@ -0,0 +1,288 @@
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
import com.eu.habbo.Emulator;
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.wired.WiredSettings;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomTile;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredEffectType;
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;
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class WiredEffectSetAltitude extends InteractionWiredEffect {
private static final Pattern ALTITUDE_PATTERN = Pattern.compile("^\\d+(\\.\\d{1,2})?$");
private static final int OPERATOR_INCREASE = 0;
private static final int OPERATOR_DECREASE = 1;
private static final int OPERATOR_SET = 2;
public static final WiredEffectType type = WiredEffectType.SET_ALTITUDE;
private final List<HabboItem> items = new ArrayList<>();
private int operator = OPERATOR_SET;
private double altitude = 0.0D;
private int furniSource = WiredSourceUtil.SOURCE_TRIGGER;
public WiredEffectSetAltitude(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredEffectSetAltitude(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public void execute(WiredContext ctx) {
Room room = ctx.room();
if (room == null) {
return;
}
List<HabboItem> effectiveItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items);
if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
this.items.removeIf(item -> item == null
|| item.getRoomId() != this.getRoomId()
|| room.getHabboItem(item.getId()) == null);
}
for (HabboItem item : effectiveItems) {
if (item == null || item.getRoomId() != this.getRoomId()) {
continue;
}
RoomTile tile = room.getLayout().getTile(item.getX(), item.getY());
if (tile == null) {
continue;
}
double nextAltitude = this.computeAltitude(item.getZ());
room.moveFurniTo(item, tile, item.getRotation(), nextAltitude, null, true, false);
}
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.getDelay(),
this.items.stream().map(HabboItem::getId).collect(Collectors.toList()),
this.operator,
this.formatAltitude(this.altitude),
this.furniSource
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.items.clear();
String wiredData = set.getString("wired_data");
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.setDelay(data.delay);
this.operator = this.normalizeOperator(data.operator);
this.altitude = this.parseAltitudeOrDefault(data.altitude);
this.furniSource = data.furniSource;
if (data.itemIds != null) {
for (Integer id : data.itemIds) {
HabboItem item = room.getHabboItem(id);
if (item != null) {
this.items.add(item);
}
}
}
return;
}
this.operator = OPERATOR_SET;
this.altitude = 0.0D;
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
this.setDelay(0);
}
@Override
public void onPickUp() {
this.items.clear();
this.operator = OPERATOR_SET;
this.altitude = 0.0D;
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
this.setDelay(0);
}
@Override
public WiredEffectType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
List<HabboItem> itemsSnapshot = new ArrayList<>(this.items);
itemsSnapshot.removeIf(item -> item == null
|| item.getRoomId() != this.getRoomId()
|| room.getHabboItem(item.getId()) == null);
this.items.clear();
this.items.addAll(itemsSnapshot);
message.appendBoolean(false);
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
message.appendInt(itemsSnapshot.size());
for (HabboItem item : itemsSnapshot) {
message.appendInt(item.getId());
}
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString(this.formatAltitude(this.altitude));
message.appendInt(2);
message.appendInt(this.operator);
message.appendInt(this.furniSource);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(this.getDelay());
message.appendInt(0);
}
@Override
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
int[] params = settings.getIntParams();
this.operator = (params.length > 0) ? this.normalizeOperator(params[0]) : OPERATOR_SET;
this.furniSource = (params.length > 1) ? params[1] : WiredSourceUtil.SOURCE_TRIGGER;
int delay = settings.getDelay();
if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) {
throw new WiredSaveException("Delay too long");
}
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
if (room == null) {
throw new WiredSaveException("Room not found");
}
if (settings.getFurniIds().length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
throw new WiredSaveException("Too many furni selected");
}
List<HabboItem> newItems = new ArrayList<>();
for (int itemId : settings.getFurniIds()) {
HabboItem item = room.getHabboItem(itemId);
if (item == null) {
throw new WiredSaveException(String.format("Item %s not found", itemId));
}
newItems.add(item);
}
this.altitude = this.parseAltitude(settings.getStringParam());
this.items.clear();
this.items.addAll(newItems);
this.setDelay(delay);
return true;
}
private int normalizeOperator(int value) {
if (value < OPERATOR_INCREASE || value > OPERATOR_SET) {
return OPERATOR_SET;
}
return value;
}
private double computeAltitude(double currentAltitude) {
double nextAltitude;
switch (this.operator) {
case OPERATOR_INCREASE:
nextAltitude = currentAltitude + this.altitude;
break;
case OPERATOR_DECREASE:
nextAltitude = currentAltitude - this.altitude;
break;
case OPERATOR_SET:
default:
nextAltitude = this.altitude;
break;
}
return this.normalizeAltitude(nextAltitude);
}
private double parseAltitude(String value) throws WiredSaveException {
String normalized = (value != null) ? value.trim() : "";
if (normalized.isEmpty()) {
return 0.0D;
}
if (!ALTITUDE_PATTERN.matcher(normalized).matches()) {
throw new WiredSaveException("Invalid altitude value");
}
try {
return this.normalizeAltitude(new BigDecimal(normalized).doubleValue());
} catch (NumberFormatException exception) {
throw new WiredSaveException("Invalid altitude value");
}
}
private double parseAltitudeOrDefault(String value) {
try {
return this.parseAltitude(value);
} catch (WiredSaveException exception) {
return 0.0D;
}
}
private 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 String formatAltitude(double value) {
BigDecimal decimal = BigDecimal.valueOf(this.normalizeAltitude(value)).stripTrailingZeros();
return (decimal.scale() < 0 ? decimal.setScale(0, RoundingMode.DOWN) : decimal).toPlainString();
}
static class JsonData {
int delay;
List<Integer> itemIds;
int operator;
String altitude;
int furniSource;
public JsonData(int delay, List<Integer> itemIds, int operator, String altitude, int furniSource) {
this.delay = delay;
this.itemIds = itemIds;
this.operator = operator;
this.altitude = altitude;
this.furniSource = furniSource;
}
}
}
@@ -197,6 +197,7 @@ public class WiredEffectToggleRandom extends InteractionWiredEffect {
try {
item.setExtradata(Emulator.getRandom().nextInt(item.getBaseItem().getStateCount() + 1) + "");
item.needsUpdate(true);
room.updateItem(item);
} catch (Exception e) {
LOGGER.error("Caught exception", e);
@@ -0,0 +1,149 @@
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
import com.eu.habbo.Emulator;
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.WiredSettings;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.wired.WiredEffectType;
import com.eu.habbo.habbohotel.wired.core.WiredContext;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
import gnu.trove.procedure.TObjectProcedure;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class WiredEffectUnfreeze extends InteractionWiredEffect {
public static final WiredEffectType type = WiredEffectType.UNFREEZE;
private int userSource = WiredSourceUtil.SOURCE_TRIGGER;
public WiredEffectUnfreeze(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredEffectUnfreeze(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public void execute(WiredContext ctx) {
Room room = ctx.room();
for (RoomUnit roomUnit : WiredSourceUtil.resolveUsers(ctx, this.userSource)) {
if (room.getHabbo(roomUnit) == null || !WiredFreezeUtil.isFrozen(roomUnit)) {
continue;
}
WiredFreezeUtil.unfreeze(room, roomUnit);
}
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(this.getDelay(), this.userSource));
}
@Override
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.setDelay(data.delay);
this.userSource = data.userSource;
} else {
this.setDelay(0);
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
}
@Override
public void onPickUp() {
this.setDelay(0);
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
@Override
public WiredEffectType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(false);
message.appendInt(5);
message.appendInt(0);
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(1);
message.appendInt(this.userSource);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(this.getDelay());
if (this.requiresTriggeringUser()) {
List<Integer> invalidTriggers = new ArrayList<>();
room.getRoomSpecialTypes().getTriggers(this.getX(), this.getY()).forEach(new TObjectProcedure<InteractionWiredTrigger>() {
@Override
public boolean execute(InteractionWiredTrigger object) {
if (!object.isTriggeredByRoomUnit()) {
invalidTriggers.add(object.getBaseItem().getSpriteId());
}
return true;
}
});
message.appendInt(invalidTriggers.size());
for (Integer i : invalidTriggers) {
message.appendInt(i);
}
} else {
message.appendInt(0);
}
}
@Override
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
int[] params = settings.getIntParams();
this.userSource = (params.length > 0) ? params[0] : WiredSourceUtil.SOURCE_TRIGGER;
int delay = settings.getDelay();
if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) {
throw new WiredSaveException("Delay too long");
}
this.setDelay(delay);
return true;
}
@Override
public boolean requiresTriggeringUser() {
return this.userSource == WiredSourceUtil.SOURCE_TRIGGER;
}
static class JsonData {
int delay;
int userSource;
public JsonData(int delay, int userSource) {
this.delay = delay;
this.userSource = userSource;
}
}
}
@@ -0,0 +1,260 @@
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
import com.eu.habbo.Emulator;
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.WiredSettings;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredEffectType;
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;
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
import gnu.trove.procedure.TObjectProcedure;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public abstract class WiredEffectUserFurniBase extends InteractionWiredEffect {
protected final List<HabboItem> items = new ArrayList<>();
protected int furniSource = WiredSourceUtil.SOURCE_TRIGGER;
protected int userSource = WiredSourceUtil.SOURCE_TRIGGER;
public WiredEffectUserFurniBase(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredEffectUserFurniBase(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
protected HabboItem resolveLastItem(WiredContext ctx) {
Room room = ctx.room();
List<HabboItem> effectiveItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items);
if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED && room != null) {
this.items.removeIf(item -> item == null
|| item.getRoomId() != this.getRoomId()
|| room.getHabboItem(item.getId()) == null);
}
if (effectiveItems.isEmpty()) {
return null;
}
for (int index = effectiveItems.size() - 1; index >= 0; index--) {
HabboItem item = effectiveItems.get(index);
if (item != null) {
return item;
}
}
return null;
}
protected Habbo resolveLastHabbo(Room room, WiredContext ctx) {
Habbo targetHabbo = null;
for (RoomUnit unit : WiredSourceUtil.resolveUsers(ctx, this.userSource)) {
Habbo habbo = room.getHabbo(unit);
if (habbo != null) {
targetHabbo = habbo;
}
}
return targetHabbo;
}
protected List<Habbo> resolveHabbos(Room room, WiredContext ctx) {
List<Habbo> habbos = new ArrayList<>();
for (RoomUnit unit : WiredSourceUtil.resolveUsers(ctx, this.userSource)) {
Habbo habbo = room.getHabbo(unit);
if (habbo != null) {
habbos.add(habbo);
}
}
return habbos;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.getDelay(),
this.items.stream().map(HabboItem::getId).collect(Collectors.toList()),
this.furniSource,
this.userSource
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.items.clear();
String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.setDelay(data.delay);
this.furniSource = data.furniSource;
this.userSource = data.userSource;
if (data.itemIds != null) {
for (Integer id : data.itemIds) {
HabboItem item = room.getHabboItem(id);
if (item != null) {
this.items.add(item);
}
}
}
if (this.furniSource == WiredSourceUtil.SOURCE_TRIGGER && !this.items.isEmpty()) {
this.furniSource = WiredSourceUtil.SOURCE_SELECTED;
}
} else {
String[] wiredDataOld = wiredData.split("\t");
if (wiredDataOld.length >= 1) {
this.setDelay(Integer.parseInt(wiredDataOld[0]));
}
if (wiredDataOld.length == 2 && wiredDataOld[1].contains(";")) {
for (String s : wiredDataOld[1].split(";")) {
HabboItem item = room.getHabboItem(Integer.parseInt(s));
if (item != null) {
this.items.add(item);
}
}
}
this.furniSource = this.items.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED;
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
}
}
@Override
public void onPickUp() {
this.items.clear();
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
this.userSource = WiredSourceUtil.SOURCE_TRIGGER;
this.setDelay(0);
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
List<HabboItem> itemsSnapshot = new ArrayList<>(this.items);
itemsSnapshot.removeIf(item -> item == null
|| item.getRoomId() != this.getRoomId()
|| room.getHabboItem(item.getId()) == null);
this.items.clear();
this.items.addAll(itemsSnapshot);
message.appendBoolean(false);
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
message.appendInt(itemsSnapshot.size());
for (HabboItem item : itemsSnapshot) {
message.appendInt(item.getId());
}
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(2);
message.appendInt(this.furniSource);
message.appendInt(this.userSource);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(this.getDelay());
if (this.requiresTriggeringUser()) {
List<Integer> invalidTriggers = new ArrayList<>();
room.getRoomSpecialTypes().getTriggers(this.getX(), this.getY()).forEach(new TObjectProcedure<InteractionWiredTrigger>() {
@Override
public boolean execute(InteractionWiredTrigger object) {
if (!object.isTriggeredByRoomUnit()) {
invalidTriggers.add(object.getId());
}
return true;
}
});
message.appendInt(invalidTriggers.size());
for (Integer i : invalidTriggers) {
message.appendInt(i);
}
} else {
message.appendInt(0);
}
}
@Override
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
this.furniSource = (settings.getIntParams().length > 0) ? settings.getIntParams()[0] : WiredSourceUtil.SOURCE_TRIGGER;
this.userSource = (settings.getIntParams().length > 1) ? settings.getIntParams()[1] : WiredSourceUtil.SOURCE_TRIGGER;
if (settings.getFurniIds().length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
throw new WiredSaveException("Too many furni selected");
}
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
if (room == null) {
throw new WiredSaveException("Room not found");
}
List<HabboItem> newItems = new ArrayList<>();
if (this.furniSource == WiredSourceUtil.SOURCE_SELECTED) {
for (int itemId : settings.getFurniIds()) {
HabboItem item = room.getHabboItem(itemId);
if (item == null) {
throw new WiredSaveException(String.format("Item %s not found", itemId));
}
newItems.add(item);
}
}
int delay = settings.getDelay();
if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) {
throw new WiredSaveException("Delay too long");
}
this.items.clear();
this.items.addAll(newItems);
this.setDelay(delay);
return true;
}
@Override
public boolean requiresTriggeringUser() {
return this.userSource == WiredSourceUtil.SOURCE_TRIGGER;
}
static class JsonData {
int delay;
List<Integer> itemIds;
int furniSource;
int userSource;
public JsonData(int delay, List<Integer> itemIds, int furniSource, int userSource) {
this.delay = delay;
this.itemIds = itemIds;
this.furniSource = furniSource;
this.userSource = userSource;
}
}
}
@@ -0,0 +1,48 @@
package com.eu.habbo.habbohotel.items.interactions.wired.effects;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredEffectType;
import com.eu.habbo.habbohotel.wired.core.WiredContext;
import java.sql.ResultSet;
import java.sql.SQLException;
public class WiredEffectUserToFurni extends WiredEffectUserFurniBase {
public static final WiredEffectType type = WiredEffectType.USER_TO_FURNI;
public WiredEffectUserToFurni(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredEffectUserToFurni(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public void execute(WiredContext ctx) {
Room room = ctx.room();
HabboItem item = this.resolveLastItem(ctx);
if (room == null || item == null) {
return;
}
for (Habbo habbo : this.resolveHabbos(room, ctx)) {
room.teleportHabboToItem(habbo, item);
}
}
@Deprecated
@Override
public boolean execute(com.eu.habbo.habbohotel.rooms.RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public WiredEffectType getType() {
return type;
}
}
@@ -112,17 +112,17 @@ public class WiredEffectFurniByType extends InteractionWiredEffect {
@Override
public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException {
int[] params = settings.getIntParams();
if (params == null || params.length < 1) {
throw new WiredSaveException("wf_slc_furni_bytype: intParams must have at least 1 element");
if (params == null || params.length < 4) {
throw new WiredSaveException("wf_slc_furni_bytype: intParams must have at least 4 elements");
}
this.sourceType = params[0];
this.sourceType = SOURCE_FURNI_PICKED;
this.matchState = params.length > 1 && params[1] == 1;
this.filterExisting = params.length > 2 && params[2] == 1;
this.invert = params.length > 3 && params[3] == 1;
this.pickedFurniIds = new ArrayList<>();
if (this.sourceType == SOURCE_FURNI_PICKED && settings.getFurniIds() != null) {
if (settings.getFurniIds() != null) {
for (int id : settings.getFurniIds()) {
if (pickedFurniIds.size() >= MAX_PICKED_FURNI) break;
pickedFurniIds.add(id);
@@ -135,12 +135,10 @@ public class WiredEffectFurniByType extends InteractionWiredEffect {
@Override
public void serializeWiredData(ServerMessage message, Room room) {
boolean pickMode = (sourceType == SOURCE_FURNI_PICKED);
message.appendBoolean(true);
message.appendInt(MAX_PICKED_FURNI);
message.appendBoolean(pickMode);
message.appendInt(pickMode ? MAX_PICKED_FURNI : 0);
if (pickMode && !pickedFurniIds.isEmpty()) {
if (!pickedFurniIds.isEmpty()) {
message.appendInt(pickedFurniIds.size());
pickedFurniIds.forEach(message::appendInt);
} else {
@@ -152,7 +150,7 @@ public class WiredEffectFurniByType extends InteractionWiredEffect {
message.appendString("");
message.appendInt(4);
message.appendInt(sourceType);
message.appendInt(SOURCE_FURNI_PICKED);
message.appendInt(matchState ? 1 : 0);
message.appendInt(filterExisting ? 1 : 0);
message.appendInt(invert ? 1 : 0);
@@ -15,38 +15,50 @@ import gnu.trove.set.hash.THashSet;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger {
private static final WiredTriggerType type = WiredTriggerType.STATE_CHANGED;
private static final int MODE_ALL_STATES = 0;
private static final int MODE_SAVED_STATE = 1;
private THashSet<HabboItem> items;
private THashSet<StateSnapshot> snapshots;
private int triggerMode = MODE_ALL_STATES;
public WiredTriggerFurniStateToggled(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
this.items = new THashSet<>();
this.snapshots = new THashSet<>();
}
public WiredTriggerFurniStateToggled(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
this.items = new THashSet<>();
this.snapshots = new THashSet<>();
}
@Override
public boolean matches(HabboItem triggerItem, WiredEvent event) {
// Reject if this was triggered by a wired effect (to prevent loops)
if (event.isTriggeredByEffect()) {
return false;
}
HabboItem sourceItem = event.getSourceItem().orElse(null);
if (sourceItem != null) {
return this.items.contains(sourceItem);
}
if (sourceItem == null) {
return false;
}
StateSnapshot snapshot = this.getSnapshot(sourceItem.getId());
if (snapshot == null) {
return false;
}
if (this.triggerMode == MODE_SAVED_STATE) {
return snapshot.state.equals(this.normalizeState(sourceItem.getExtradata()));
}
return true;
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
@@ -56,21 +68,36 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger {
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.items.stream().map(HabboItem::getId).collect(Collectors.toList())
this.triggerMode,
new ArrayList<>(this.snapshots)
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.items = new THashSet<>();
this.snapshots = new THashSet<>();
this.triggerMode = MODE_ALL_STATES;
String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) {
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
for (Integer id: data.itemIds) {
this.triggerMode = (data != null) ? data.triggerMode : MODE_ALL_STATES;
if (data != null && data.snapshots != null && !data.snapshots.isEmpty()) {
for (StateSnapshot snapshot : data.snapshots) {
if (snapshot == null) continue;
HabboItem item = room.getHabboItem(snapshot.itemId);
if (item != null) {
this.snapshots.add(new StateSnapshot(item.getId(), snapshot.state));
}
}
} else if (data != null && data.itemIds != null) {
for (Integer id : data.itemIds) {
HabboItem item = room.getHabboItem(id);
if (item != null) {
this.items.add(item);
this.snapshots.add(this.captureSnapshot(item));
}
}
}
} else {
@@ -79,10 +106,15 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger {
if (!wiredData.split(":")[2].equals("\t")) {
for (String s : wiredData.split(":")[2].split(";")) {
if (s.isEmpty()) {
continue;
}
HabboItem item = room.getHabboItem(Integer.parseInt(s));
if (item != null)
this.items.add(item);
if (item != null) {
this.snapshots.add(this.captureSnapshot(item));
}
}
}
}
@@ -91,7 +123,8 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger {
@Override
public void onPickUp() {
this.items.clear();
this.snapshots.clear();
this.triggerMode = MODE_ALL_STATES;
}
@Override
@@ -101,33 +134,31 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger {
@Override
public void serializeWiredData(ServerMessage message, Room room) {
THashSet<HabboItem> items = new THashSet<>();
THashSet<StateSnapshot> snapshotsToRemove = new THashSet<>();
for (HabboItem item : this.items) {
if (item.getRoomId() != this.getRoomId()) {
items.add(item);
for (StateSnapshot snapshot : this.snapshots) {
HabboItem item = room.getHabboItem(snapshot.itemId);
if (item == null || item.getRoomId() != this.getRoomId()) {
snapshotsToRemove.add(snapshot);
continue;
}
if (room.getHabboItem(item.getId()) == null) {
items.add(item);
}
}
for (HabboItem item : items) {
this.items.remove(item);
for (StateSnapshot snapshot : snapshotsToRemove) {
this.snapshots.remove(snapshot);
}
message.appendBoolean(false);
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
message.appendInt(this.items.size());
for (HabboItem item : this.items) {
message.appendInt(item.getId());
message.appendInt(this.snapshots.size());
for (StateSnapshot snapshot : this.snapshots) {
message.appendInt(snapshot.itemId);
}
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(0);
message.appendInt(1);
message.appendInt(this.triggerMode);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(0);
@@ -135,14 +166,22 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger {
@Override
public boolean saveData(WiredSettings settings) {
this.items.clear();
this.snapshots.clear();
this.triggerMode = (settings.getIntParams().length > 0 && settings.getIntParams()[0] == MODE_SAVED_STATE)
? MODE_SAVED_STATE
: MODE_ALL_STATES;
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
if (room == null) {
return true;
}
int count = settings.getFurniIds().length;
for (int i = 0; i < count; i++) {
HabboItem item = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()).getHabboItem(settings.getFurniIds()[i]);
HabboItem item = room.getHabboItem(settings.getFurniIds()[i]);
if (item != null) {
this.items.add(item);
this.snapshots.add(this.captureSnapshot(item));
}
}
@@ -154,11 +193,71 @@ public class WiredTriggerFurniStateToggled extends InteractionWiredTrigger {
return true;
}
private StateSnapshot captureSnapshot(HabboItem item) {
return new StateSnapshot(item.getId(), this.normalizeState(item.getExtradata()));
}
private StateSnapshot getSnapshot(int itemId) {
for (StateSnapshot snapshot : this.snapshots) {
if (snapshot.itemId == itemId) {
return snapshot;
}
}
return null;
}
private String normalizeState(String state) {
return (state == null) ? "" : state;
}
static class JsonData {
int triggerMode;
List<StateSnapshot> snapshots;
List<Integer> itemIds;
public JsonData() {
}
public JsonData(List<Integer> itemIds) {
this.itemIds = itemIds;
}
public JsonData(int triggerMode, List<StateSnapshot> snapshots) {
this.triggerMode = triggerMode;
this.snapshots = snapshots;
}
}
static class StateSnapshot {
int itemId;
String state;
public StateSnapshot() {
}
public StateSnapshot(int itemId, String state) {
this.itemId = itemId;
this.state = (state == null) ? "" : state;
}
@Override
public int hashCode() {
return Integer.hashCode(this.itemId);
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof StateSnapshot)) {
return false;
}
StateSnapshot that = (StateSnapshot) object;
return this.itemId == that.itemId;
}
}
}
@@ -0,0 +1,164 @@
package com.eu.habbo.habbohotel.items.interactions.wired.triggers;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
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.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredTriggerType;
import com.eu.habbo.habbohotel.wired.core.WiredEvent;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.messages.ServerMessage;
import gnu.trove.set.hash.THashSet;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.stream.Collectors;
public class WiredTriggerHabboClicksFurni extends InteractionWiredTrigger {
public static final WiredTriggerType type = WiredTriggerType.CLICKS_FURNI;
private THashSet<HabboItem> items;
public WiredTriggerHabboClicksFurni(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
this.items = new THashSet<>();
}
public WiredTriggerHabboClicksFurni(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
this.items = new THashSet<>();
}
@Override
public boolean matches(HabboItem triggerItem, WiredEvent event) {
HabboItem sourceItem = event.getSourceItem().orElse(null);
return sourceItem != null && this.items.contains(sourceItem);
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public WiredTriggerType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
THashSet<HabboItem> items = new THashSet<>();
if (Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()) == null) {
items.addAll(this.items);
} else {
for (HabboItem item : this.items) {
if (Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()).getHabboItem(item.getId()) == null) {
items.add(item);
}
}
}
for (HabboItem item : items) {
this.items.remove(item);
}
message.appendBoolean(false);
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
message.appendInt(this.items.size());
for (HabboItem item : this.items) {
message.appendInt(item.getId());
}
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(0);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(0);
message.appendInt(0);
}
@Override
public boolean saveData(WiredSettings settings) {
this.items.clear();
int count = settings.getFurniIds().length;
for (int i = 0; i < count; i++) {
HabboItem item = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()).getHabboItem(settings.getFurniIds()[i]);
if (item != null) {
this.items.add(item);
}
}
return true;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.items.stream().map(HabboItem::getId).collect(Collectors.toList())
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.items.clear();
String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
for (Integer id : data.itemIds) {
HabboItem item = room.getHabboItem(id);
if (item != null) {
this.items.add(item);
}
}
} else {
if (wiredData.split(":").length >= 3) {
super.setDelay(Integer.parseInt(wiredData.split(":")[0]));
if (!wiredData.split(":")[2].equals("\t")) {
for (String s : wiredData.split(":")[2].split(";")) {
if (s.isEmpty()) {
continue;
}
try {
HabboItem item = room.getHabboItem(Integer.parseInt(s));
if (item != null) {
this.items.add(item);
}
} catch (Exception e) {
}
}
}
}
}
}
@Override
public void onPickUp() {
this.items.clear();
}
@Override
public boolean isTriggeredByRoomUnit() {
return true;
}
static class JsonData {
List<Integer> itemIds;
public JsonData(List<Integer> itemIds) {
this.itemIds = itemIds;
}
}
}
@@ -0,0 +1,47 @@
package com.eu.habbo.habbohotel.items.interactions.wired.triggers;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredTriggerType;
import com.eu.habbo.habbohotel.wired.core.WiredEvent;
import java.sql.ResultSet;
import java.sql.SQLException;
public class WiredTriggerHabboClicksTile extends WiredTriggerHabboClicksFurni {
public static final WiredTriggerType type = WiredTriggerType.CLICKS_TILE;
private static final String CLICK_TILE_INTERACTION = "room_invisible_click_tile";
public WiredTriggerHabboClicksTile(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredTriggerHabboClicksTile(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public boolean matches(HabboItem triggerItem, WiredEvent event) {
if (!super.matches(triggerItem, event)) {
return false;
}
HabboItem sourceItem = event.getSourceItem().orElse(null);
return isClickTileItem(sourceItem);
}
@Override
public WiredTriggerType getType() {
return type;
}
private boolean isClickTileItem(HabboItem item) {
if (item == null || item.getBaseItem() == null || item.getBaseItem().getInteractionType() == null) {
return false;
}
String interaction = item.getBaseItem().getInteractionType().getName();
return interaction != null && interaction.equalsIgnoreCase(CLICK_TILE_INTERACTION);
}
}
@@ -0,0 +1,80 @@
package com.eu.habbo.habbohotel.items.interactions.wired.triggers;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
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.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredTriggerType;
import com.eu.habbo.habbohotel.wired.core.WiredEvent;
import com.eu.habbo.messages.ServerMessage;
import java.sql.ResultSet;
import java.sql.SQLException;
public class WiredTriggerHabboClicksUser extends InteractionWiredTrigger {
public static final WiredTriggerType type = WiredTriggerType.CLICKS_USER;
public WiredTriggerHabboClicksUser(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredTriggerHabboClicksUser(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public boolean matches(HabboItem triggerItem, WiredEvent event) {
return event.getActor().isPresent();
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return "";
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
}
@Override
public void onPickUp() {
}
@Override
public WiredTriggerType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(false);
message.appendInt(5);
message.appendInt(0);
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(0);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(0);
message.appendInt(0);
}
@Override
public boolean saveData(WiredSettings settings) {
return true;
}
@Override
public boolean isTriggeredByRoomUnit() {
return true;
}
}
@@ -0,0 +1,116 @@
package com.eu.habbo.habbohotel.items.interactions.wired.triggers;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
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.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.WiredTriggerType;
import com.eu.habbo.habbohotel.wired.core.WiredEvent;
import com.eu.habbo.messages.ServerMessage;
import java.sql.ResultSet;
import java.sql.SQLException;
public class WiredTriggerHabboLeavesRoom extends InteractionWiredTrigger {
public static final WiredTriggerType type = WiredTriggerType.LEAVE_ROOM;
private String username = "";
public WiredTriggerHabboLeavesRoom(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredTriggerHabboLeavesRoom(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public boolean matches(HabboItem triggerItem, WiredEvent event) {
RoomUnit roomUnit = event.getActor().orElse(null);
Room room = event.getRoom();
Habbo habbo = room.getHabbo(roomUnit);
if (habbo != null) {
if (this.username.length() > 0) {
return habbo.getHabboInfo().getUsername().equalsIgnoreCase(this.username);
}
return true;
}
return false;
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.username
));
}
@Override
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.username = data.username;
} else {
this.username = wiredData;
}
}
@Override
public void onPickUp() {
this.username = "";
}
@Override
public WiredTriggerType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(false);
message.appendInt(5);
message.appendInt(0);
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString(this.username);
message.appendInt(0);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(0);
message.appendInt(0);
}
@Override
public boolean saveData(WiredSettings settings) {
this.username = settings.getStringParam();
return true;
}
@Override
public boolean isTriggeredByRoomUnit() {
return true;
}
static class JsonData {
String username;
public JsonData(String username) {
this.username = username;
}
}
}
@@ -0,0 +1,217 @@
package com.eu.habbo.habbohotel.items.interactions.wired.triggers;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
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.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredTriggerType;
import com.eu.habbo.habbohotel.wired.WiredUserActionType;
import com.eu.habbo.habbohotel.wired.core.WiredEvent;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.messages.ServerMessage;
import java.sql.ResultSet;
import java.sql.SQLException;
public class WiredTriggerHabboPerformsAction extends InteractionWiredTrigger {
private static final WiredTriggerType type = WiredTriggerType.USER_PERFORMS_ACTION;
private static final int DEFAULT_ACTION = WiredUserActionType.WAVE;
private int selectedAction = DEFAULT_ACTION;
private boolean signFilterEnabled = false;
private int signId = 0;
private boolean danceFilterEnabled = false;
private int danceId = 1;
public WiredTriggerHabboPerformsAction(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
}
public WiredTriggerHabboPerformsAction(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
}
@Override
public boolean matches(HabboItem triggerItem, WiredEvent event) {
if (!event.getActor().isPresent()) {
return false;
}
if (event.getActionId() != this.selectedAction) {
return false;
}
if (this.selectedAction == WiredUserActionType.SIGN && this.signFilterEnabled) {
return event.getActionParameter() == this.signId;
}
if (this.selectedAction == WiredUserActionType.DANCE && this.danceFilterEnabled) {
return event.getActionParameter() == this.danceId;
}
return true;
}
@Deprecated
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.selectedAction,
this.signFilterEnabled,
this.signId,
this.danceFilterEnabled,
this.danceId
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.resetSettings();
String wiredData = set.getString("wired_data");
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
if (data == null) {
return;
}
this.selectedAction = normalizeAction(data.selectedAction);
this.signFilterEnabled = data.signFilterEnabled;
this.signId = normalizeSignId(data.signId);
this.danceFilterEnabled = data.danceFilterEnabled;
this.danceId = normalizeDanceId(data.danceId);
}
}
@Override
public void onPickUp() {
this.resetSettings();
}
@Override
public WiredTriggerType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(false);
message.appendInt(5);
message.appendInt(0);
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(5);
message.appendInt(this.selectedAction);
message.appendInt(this.signFilterEnabled ? 1 : 0);
message.appendInt(this.signId);
message.appendInt(this.danceFilterEnabled ? 1 : 0);
message.appendInt(this.danceId);
message.appendInt(0);
message.appendInt(this.getType().code);
message.appendInt(0);
message.appendInt(0);
}
@Override
public boolean saveData(WiredSettings settings) {
int[] intParams = settings.getIntParams();
this.resetSettings();
if (intParams.length > 0) {
this.selectedAction = normalizeAction(intParams[0]);
}
if (intParams.length > 1) {
this.signFilterEnabled = (intParams[1] == 1);
}
if (intParams.length > 2) {
this.signId = normalizeSignId(intParams[2]);
}
if (intParams.length > 3) {
this.danceFilterEnabled = (intParams[3] == 1);
}
if (intParams.length > 4) {
this.danceId = normalizeDanceId(intParams[4]);
}
return true;
}
@Override
public boolean isTriggeredByRoomUnit() {
return true;
}
private void resetSettings() {
this.selectedAction = DEFAULT_ACTION;
this.signFilterEnabled = false;
this.signId = 0;
this.danceFilterEnabled = false;
this.danceId = 1;
}
private int normalizeAction(int action) {
switch (action) {
case WiredUserActionType.WAVE:
case WiredUserActionType.BLOW_KISS:
case WiredUserActionType.LAUGH:
case WiredUserActionType.AWAKE:
case WiredUserActionType.RELAX:
case WiredUserActionType.SIT:
case WiredUserActionType.STAND:
case WiredUserActionType.LAY:
case WiredUserActionType.SIGN:
case WiredUserActionType.DANCE:
case WiredUserActionType.THUMB_UP:
return action;
default:
return DEFAULT_ACTION;
}
}
private int normalizeSignId(int signId) {
if (signId < 0 || signId > 17) {
return 0;
}
return signId;
}
private int normalizeDanceId(int danceId) {
if (danceId < 1 || danceId > 4) {
return 1;
}
return danceId;
}
static class JsonData {
int selectedAction;
boolean signFilterEnabled;
int signId;
boolean danceFilterEnabled;
int danceId;
public JsonData(int selectedAction, boolean signFilterEnabled, int signId, boolean danceFilterEnabled, int danceId) {
this.selectedAction = selectedAction;
this.signFilterEnabled = signFilterEnabled;
this.signId = signId;
this.danceFilterEnabled = danceFilterEnabled;
this.danceId = danceId;
}
}
}
@@ -1,5 +1,6 @@
package com.eu.habbo.habbohotel.items.interactions.wired.triggers;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
@@ -11,27 +12,49 @@ import com.eu.habbo.habbohotel.wired.WiredTriggerType;
import com.eu.habbo.habbohotel.wired.core.WiredEvent;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.incoming.wired.WiredTriggerSaveException;
import com.eu.habbo.messages.outgoing.rooms.items.ItemStateComposer;
import gnu.trove.set.hash.THashSet;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
public class WiredTriggerReceiveSignal extends InteractionWiredTrigger {
public static final WiredTriggerType type = WiredTriggerType.RECEIVE_SIGNAL;
private static final String ANTENNA_INTERACTION = "antenna";
private static final long ACTIVATION_PULSE_MS = 300L;
private int channel = 0; // signal channel (0-based)
private THashSet<HabboItem> items;
private final AtomicLong activationToken = new AtomicLong();
public WiredTriggerReceiveSignal(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
this.items = new THashSet<>();
}
public WiredTriggerReceiveSignal(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
this.items = new THashSet<>();
}
@Override
public boolean matches(HabboItem triggerItem, WiredEvent event) {
return event.getType() == WiredEvent.Type.SIGNAL_RECEIVED
&& event.getSignalChannel() == this.channel;
if (event.getType() != WiredEvent.Type.SIGNAL_RECEIVED) return false;
if (!this.items.isEmpty()) {
int signalChannel = event.getSignalChannel();
for (HabboItem antenna : this.items) {
if (antenna != null && antenna.getId() == signalChannel) return true;
}
return false;
}
return event.getSignalChannel() == this.channel;
}
public int getChannel() {
@@ -59,14 +82,33 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger {
int senderCount = 0;
try {
if (room != null && room.getRoomSpecialTypes() != null) {
if (!this.items.isEmpty()) {
for (HabboItem item : this.items) {
senderCount += room.getRoomSpecialTypes().countSendersTargetingReceiver(item.getId());
}
} else {
senderCount = room.getRoomSpecialTypes().countSendersTargetingReceiver(this.getId());
}
}
} catch (Exception e) {
}
THashSet<HabboItem> itemsToRemove = new THashSet<>();
for (HabboItem item : this.items) {
if (item.getRoomId() != this.getRoomId() || room.getHabboItem(item.getId()) == null) {
itemsToRemove.add(item);
}
}
for (HabboItem item : itemsToRemove) {
this.items.remove(item);
}
message.appendBoolean(false);
message.appendInt(0);
message.appendInt(0);
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
message.appendInt(this.items.size());
for (HabboItem item : this.items) {
message.appendInt(item.getId());
}
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
@@ -82,37 +124,100 @@ public class WiredTriggerReceiveSignal extends InteractionWiredTrigger {
@Override
public boolean saveData(WiredSettings settings) {
this.items.clear();
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
int count = settings.getFurniIds().length;
for (int i = 0; i < count; i++) {
HabboItem item = room.getHabboItem(settings.getFurniIds()[i]);
if (item == null) continue;
if (!isAntennaItem(item)) throw new WiredTriggerSaveException("wiredfurni.error.require_antenna_furni");
this.items.add(item);
}
int[] params = settings.getIntParams();
this.channel = params.length > 0 ? params[0] : 0;
return true;
}
@Override
public void activateBox(Room room, RoomUnit roomUnit, long millis) {
if (roomUnit != null) {
this.addUserExecutionCache(roomUnit.getId(), millis);
}
if (room == null || room.isHideWired() || this.getBaseItem().getStateCount() <= 1) {
return;
}
final long token = this.activationToken.incrementAndGet();
if ("1".equals(this.getExtradata())) {
this.setExtradata("0");
room.sendComposer(new ItemStateComposer(this).compose());
}
this.setExtradata("1");
room.sendComposer(new ItemStateComposer(this).compose());
Emulator.getThreading().run(() -> {
if (!room.isLoaded()) return;
if (this.activationToken.get() != token) return;
this.setExtradata("0");
room.sendComposer(new ItemStateComposer(this).compose());
}, ACTIVATION_PULSE_MS);
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(channel));
return WiredManager.getGson().toJson(new JsonData(
channel,
this.items.stream().map(HabboItem::getId).collect(Collectors.toList())
));
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.items = new THashSet<>();
String wiredData = set.getString("wired_data");
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.channel = data.channel;
if (data.itemIds != null) {
for (Integer id : data.itemIds) {
HabboItem item = room.getHabboItem(id);
if (item != null) this.items.add(item);
}
}
}
}
@Override
public void onPickUp() {
this.channel = 0;
this.items.clear();
}
private boolean isAntennaItem(HabboItem item) {
if (item == null || item.getBaseItem() == null || item.getBaseItem().getInteractionType() == null) return false;
String interaction = item.getBaseItem().getInteractionType().getName();
if (interaction == null) return false;
String normalized = interaction.toLowerCase();
return normalized.equals(ANTENNA_INTERACTION);
}
static class JsonData {
int channel;
List<Integer> itemIds;
public JsonData() {}
public JsonData(int channel) {
public JsonData(int channel, List<Integer> itemIds) {
this.channel = channel;
this.itemIds = itemIds;
}
}
}
@@ -139,7 +139,7 @@ public class WiredTriggerRepeaterLong extends InteractionWiredTrigger implements
// Fire when elapsed time is a multiple of repeat time
if (elapsedMs % this.repeatTime == 0) {
if (this.getRoomId() != 0 && room.isLoaded()) {
WiredManager.triggerTimerRepeat(room, this);
WiredManager.triggerTimerRepeatLong(room, this);
}
}
}
@@ -0,0 +1,125 @@
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.rooms.Room;
import com.eu.habbo.habbohotel.wired.WiredTriggerType;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.messages.ServerMessage;
import gnu.trove.procedure.TObjectProcedure;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class WiredTriggerRepeaterShort extends WiredTriggerRepeater {
public static final WiredTriggerType type = WiredTriggerType.PERIODICALLY_SHORT;
public static final int STEP_MS = 50;
public static final int DEFAULT_DELAY = 10 * STEP_MS;
public static final int MIN_DELAY = STEP_MS;
public static final int MAX_DELAY = 10 * STEP_MS;
public WiredTriggerRepeaterShort(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
this.repeatTime = DEFAULT_DELAY;
}
public WiredTriggerRepeaterShort(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
this.repeatTime = DEFAULT_DELAY;
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
String wiredData = set.getString("wired_data");
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.repeatTime = (data != null) ? data.repeatTime : DEFAULT_DELAY;
} else if (wiredData != null && wiredData.length() >= 1) {
this.repeatTime = Integer.parseInt(wiredData);
} else {
this.repeatTime = DEFAULT_DELAY;
}
this.repeatTime = clampRepeatTime(this.repeatTime);
}
@Override
public void onPickUp() {
this.repeatTime = DEFAULT_DELAY;
}
@Override
public WiredTriggerType getType() {
return type;
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
message.appendBoolean(false);
message.appendInt(5);
message.appendInt(0);
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString("");
message.appendInt(1);
message.appendInt(this.repeatTime / STEP_MS);
message.appendInt(0);
message.appendInt(this.getType().code);
if (!this.isTriggeredByRoomUnit()) {
List<Integer> invalidTriggers = new ArrayList<>();
room.getRoomSpecialTypes().getEffects(this.getX(), this.getY()).forEach(new TObjectProcedure<InteractionWiredEffect>() {
@Override
public boolean execute(InteractionWiredEffect object) {
if (object.requiresTriggeringUser()) {
invalidTriggers.add(object.getBaseItem().getSpriteId());
}
return true;
}
});
message.appendInt(invalidTriggers.size());
for (Integer i : invalidTriggers) {
message.appendInt(i);
}
} else {
message.appendInt(0);
}
}
@Override
public boolean saveData(WiredSettings settings) {
if (settings.getIntParams().length < 1) return false;
int newRepeatTime = settings.getIntParams()[0] * STEP_MS;
this.repeatTime = clampRepeatTime(newRepeatTime);
return true;
}
@Override
public void onWiredTick(Room room, long tickCount, int tickIntervalMs) {
long elapsedMs = tickCount * tickIntervalMs;
if (elapsedMs % this.repeatTime == 0) {
if (this.getRoomId() != 0 && room.isLoaded()) {
WiredManager.triggerTimerRepeatShort(room, this);
}
}
}
private int clampRepeatTime(int repeatTime) {
if (repeatTime < MIN_DELAY) {
return DEFAULT_DELAY;
}
if (repeatTime > MAX_DELAY) {
return MAX_DELAY;
}
return repeatTime;
}
}
@@ -15,6 +15,8 @@ import com.eu.habbo.habbohotel.pets.PetManager;
import com.eu.habbo.habbohotel.users.DanceType;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredUserActionType;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.messages.ISerialize;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.outgoing.guilds.GuildInfoComposer;
@@ -1874,11 +1876,15 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
}
public void muteHabbo(Habbo habbo, int minutes) {
this.rightsManager.muteHabbo(habbo, minutes);
this.chatManager.muteHabbo(habbo, minutes);
}
public void unmuteHabbo(Habbo habbo) {
this.chatManager.unmuteHabbo(habbo);
}
public boolean isMuted(Habbo habbo) {
return this.rightsManager.isMuted(habbo);
return this.chatManager.isMuted(habbo);
}
public void habboEntered(Habbo habbo) {
@@ -2162,6 +2168,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
- habbo.getRoomUnit().getBodyRotation().getValue() % 2]);
habbo.getRoomUnit().setStatus(RoomUnitStatus.SIT, 0.5 + "");
this.sendComposer(new RoomUserStatusComposer(habbo.getRoomUnit()).compose());
WiredManager.triggerUserPerformsAction(this, habbo.getRoomUnit(), WiredUserActionType.SIT, -1);
}
public void makeStand(Habbo habbo) {
@@ -2171,12 +2178,19 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
HabboItem item = this.getTopItemAt(habbo.getRoomUnit().getX(), habbo.getRoomUnit().getY());
if (item == null || !item.getBaseItem().allowSit() || !item.getBaseItem().allowLay()) {
boolean wasSittingOrLaying = habbo.getRoomUnit().hasStatus(RoomUnitStatus.SIT)
|| habbo.getRoomUnit().hasStatus(RoomUnitStatus.LAY);
habbo.getRoomUnit().cmdStand = true;
habbo.getRoomUnit().setBodyRotation(
RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue()
- habbo.getRoomUnit().getBodyRotation().getValue() % 2]);
habbo.getRoomUnit().removeStatus(RoomUnitStatus.SIT);
habbo.getRoomUnit().removeStatus(RoomUnitStatus.LAY);
this.sendComposer(new RoomUserStatusComposer(habbo.getRoomUnit()).compose());
if (wasSittingOrLaying) {
WiredManager.triggerUserPerformsAction(this, habbo.getRoomUnit(), WiredUserActionType.STAND, -1);
}
}
}
@@ -156,6 +156,15 @@ public class RoomChatManager {
}
}
/**
* Removes a room mute from a Habbo.
*/
public void unmuteHabbo(Habbo habbo) {
synchronized (this.mutedHabbos) {
this.mutedHabbos.remove(habbo.getHabboInfo().getId());
}
}
/**
* Checks if a Habbo is muted.
*/
@@ -183,7 +192,8 @@ public class RoomChatManager {
*/
public int getMuteTimeRemaining(Habbo habbo) {
if (this.mutedHabbos.containsKey(habbo.getHabboInfo().getId())) {
return this.mutedHabbos.get(habbo.getHabboInfo().getId()) - Emulator.getIntUnixTimestamp();
return Math.max(0,
this.mutedHabbos.get(habbo.getHabboInfo().getId()) - Emulator.getIntUnixTimestamp());
}
return 0;
}
@@ -298,7 +308,7 @@ public class RoomChatManager {
if (this.isMuted(habbo)) {
habbo.getClient().sendResponse(new MutedWhisperComposer(
this.mutedHabbos.get(habbo.getHabboInfo().getId()) - Emulator.getIntUnixTimestamp()));
Math.max(1, this.getMuteTimeRemaining(habbo))));
return;
}
}
@@ -499,14 +499,18 @@ public class RoomManager {
}
public void enterRoom(Habbo habbo, int roomId, String password) {
this.enterRoom(habbo, roomId, password, false, null);
this.enterRoom(habbo, roomId, password, false, null, false);
}
public void enterRoom(Habbo habbo, int roomId, String password, boolean overrideChecks) {
this.enterRoom(habbo, roomId, password, overrideChecks, null);
this.enterRoom(habbo, roomId, password, overrideChecks, null, false);
}
public void enterRoom(Habbo habbo, int roomId, String password, boolean overrideChecks, RoomTile doorLocation) {
this.enterRoom(habbo, roomId, password, overrideChecks, doorLocation, false);
}
public void enterRoom(Habbo habbo, int roomId, String password, boolean overrideChecks, RoomTile doorLocation, boolean isReconnectSpawn) {
Room room = this.loadRoom(roomId, true);
if (room == null)
@@ -547,7 +551,7 @@ public class RoomManager {
room.hasRights(habbo) ||
(room.getState().equals(RoomState.INVISIBLE) && room.hasRights(habbo)) ||
(room.hasGuild() && room.getGuildRightLevel(habbo).isGreaterThan(RoomRightLevels.GUILD_RIGHTS))) {
this.openRoom(habbo, room, doorLocation);
this.openRoom(habbo, room, doorLocation, isReconnectSpawn);
} else if (room.getState() == RoomState.LOCKED) {
boolean rightsFound = false;
@@ -572,7 +576,7 @@ public class RoomManager {
room.addToQueue(habbo);
} else if (room.getState() == RoomState.PASSWORD) {
if (room.getPassword().equalsIgnoreCase(password))
this.openRoom(habbo, room, doorLocation);
this.openRoom(habbo, room, doorLocation, isReconnectSpawn);
else {
habbo.getClient().sendResponse(new GenericErrorMessagesComposer(GenericErrorMessagesComposer.WRONG_PASSWORD_USED));
habbo.getClient().sendResponse(new HotelViewComposer());
@@ -585,6 +589,10 @@ public class RoomManager {
}
void openRoom(Habbo habbo, Room room, RoomTile doorLocation) {
this.openRoom(habbo, room, doorLocation, false);
}
void openRoom(Habbo habbo, Room room, RoomTile doorLocation, boolean isReconnectSpawn) {
if (room == null || room.getLayout() == null)
return;
@@ -623,7 +631,13 @@ public class RoomManager {
if (doorLocation == null) {
habbo.getRoomUnit().setBodyRotation(RoomUserRotation.values()[room.getLayout().getDoorDirection()]);
habbo.getRoomUnit().setHeadRotation(RoomUserRotation.values()[room.getLayout().getDoorDirection()]);
} else if (isReconnectSpawn) {
// Reconnect spawn: place at tile but keep normal room behavior
// (user can still leave by door, no teleport flags)
habbo.getRoomUnit().setBodyRotation(RoomUserRotation.values()[room.getLayout().getDoorDirection()]);
habbo.getRoomUnit().setHeadRotation(RoomUserRotation.values()[room.getLayout().getDoorDirection()]);
} else {
// Furniture teleport spawn
habbo.getRoomUnit().setCanLeaveRoomByDoor(false);
habbo.getRoomUnit().isTeleporting = true;
HabboItem topItem = room.getTopItemAt(doorLocation.x, doorLocation.y);
@@ -12,7 +12,9 @@ import com.eu.habbo.habbohotel.users.DanceType;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboGender;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.WiredUserActionType;
import com.eu.habbo.messages.outgoing.generic.alerts.GenericErrorMessagesComposer;
import com.eu.habbo.messages.outgoing.inventory.AddPetComposer;
import com.eu.habbo.messages.outgoing.rooms.pets.RoomPetComposer;
@@ -217,6 +219,10 @@ public class RoomUnitManager {
return;
}
if (habbo.getRoomUnit() != null) {
WiredManager.triggerUserLeavesRoom(this.room, habbo.getRoomUnit());
}
if (habbo.getRoomUnit() != null && habbo.getRoomUnit().getCurrentLocation() != null) {
habbo.getRoomUnit().getCurrentLocation().removeUnit(habbo.getRoomUnit());
}
@@ -352,6 +358,7 @@ public class RoomUnitManager {
}
double z = habbo.getRoomUnit().getCurrentLocation().getStackHeight();
boolean hadLayStatus = habbo.getRoomUnit().hasStatus(RoomUnitStatus.LAY);
if (habbo.getRoomUnit().hasStatus(RoomUnitStatus.SIT)
|| (topItem != null && topItem.getBaseItem().allowSit())) {
@@ -413,6 +420,10 @@ public class RoomUnitManager {
}
habbo.getRoomUnit().statusUpdate(true);
if (!hadLayStatus && habbo.getRoomUnit().hasStatus(RoomUnitStatus.LAY)) {
WiredManager.triggerUserPerformsAction(this.room, habbo.getRoomUnit(), WiredUserActionType.LAY, -1);
}
}
if (!habbos.isEmpty()) {
@@ -1299,6 +1310,8 @@ public class RoomUnitManager {
*/
public void teleportRoomUnitToLocation(RoomUnit roomUnit, short x, short y, double z) {
if (this.room.isLoaded()) {
WiredFreezeUtil.onTeleport(this.room, roomUnit);
RoomTile tile = this.room.getLayout().getTile(x, y);
if (z < tile.z) {
@@ -1310,6 +1323,7 @@ public class RoomUnitManager {
roomUnit.setZ(z);
roomUnit.setPreviousLocationZ(z);
this.room.updateRoomUnit(roomUnit);
WiredFreezeUtil.restoreWalkState(roomUnit);
}
}
@@ -25,7 +25,18 @@ public enum WiredConditionType {
NOT_ACTOR_WEARS_EFFECT(23),
DATE_RANGE(24),
ACTOR_HAS_HANDITEM(25),
MOVEMENT_VALIDATION(26); // i dont know what type it is but its needed
MOVEMENT_VALIDATION(26), // i dont know what type it is but its needed
COUNTER_TIME_MATCHES(27),
USER_PERFORMS_ACTION(28),
HAS_ALTITUDE(29),
NOT_USER_PERFORMS_ACTION(30),
NOT_ACTOR_HAS_HANDITEM(31),
TRIGGERER_MATCH(32),
NOT_TRIGGERER_MATCH(33),
TEAM_HAS_SCORE(34),
TEAM_HAS_RANK(35),
MATCH_TIME(36),
MATCH_DATE(37);
public final int code;
@@ -32,7 +32,14 @@ public enum WiredEffectType {
FURNI_BYTYPE_SELECTOR(30),
USERS_AREA_SELECTOR(31),
USERS_NEIGHBORHOOD_SELECTOR(32),
SEND_SIGNAL(33);
SEND_SIGNAL(33),
FREEZE(34),
UNFREEZE(35),
FURNI_TO_USER(36),
USER_TO_FURNI(37),
FURNI_TO_FURNI(38),
SET_ALTITUDE(39),
RELATIVE_MOVE(40);
public final int code;
@@ -285,53 +285,72 @@ public class WiredHandler {
}
}
private static void giveReward(Habbo habbo, WiredEffectGiveReward wiredBox, WiredGiveRewardItem reward) {
if (wiredBox.limit > 0)
wiredBox.given++;
private static void persistReward(int wiredId, int habboId, int rewardId, int timestamp) {
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO wired_rewards_given (wired_item, user_id, reward_id, timestamp) VALUES ( ?, ?, ?, ?)")) {
statement.setInt(1, wiredBox.getId());
statement.setInt(2, habbo.getHabboInfo().getId());
statement.setInt(3, reward.id);
statement.setInt(4, Emulator.getIntUnixTimestamp());
statement.setInt(1, wiredId);
statement.setInt(2, habboId);
statement.setInt(3, rewardId);
statement.setInt(4, timestamp);
statement.execute();
} catch (SQLException e) {
LOGGER.error("Caught SQL exception", e);
}
}
private static void completeReward(Habbo habbo, WiredEffectGiveReward wiredBox, WiredGiveRewardItem reward, int successCode) {
if (wiredBox.limit > 0)
wiredBox.given++;
persistReward(wiredBox.getId(), habbo.getHabboInfo().getId(), reward.id, Emulator.getIntUnixTimestamp());
habbo.getClient().sendResponse(new WiredRewardAlertComposer(successCode));
}
private static boolean giveReward(Habbo habbo, WiredEffectGiveReward wiredBox, WiredGiveRewardItem reward) {
if (reward.badge) {
UserWiredRewardReceived rewardReceived = new UserWiredRewardReceived(habbo, wiredBox, "badge", reward.data);
if (Emulator.getPluginManager().fireEvent(rewardReceived).isCancelled())
return;
return false;
if (rewardReceived.value.isEmpty())
return;
return false;
if (habbo.getInventory().getBadgesComponent().hasBadge(rewardReceived.value))
return;
if (habbo.getInventory().getBadgesComponent().hasBadge(rewardReceived.value)) {
habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_ALREADY_RECEIVED));
return false;
}
HabboBadge badge = new HabboBadge(0, rewardReceived.value, 0, habbo);
Emulator.getThreading().run(badge);
habbo.getInventory().getBadgesComponent().addBadge(badge);
habbo.getClient().sendResponse(new AddUserBadgeComposer(badge));
habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_RECEIVED_BADGE));
} else {
completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_BADGE);
return true;
}
String[] data = reward.data.split("#");
if (data.length == 2) {
if (data.length != 2)
return false;
UserWiredRewardReceived rewardReceived = new UserWiredRewardReceived(habbo, wiredBox, data[0], data[1]);
if (Emulator.getPluginManager().fireEvent(rewardReceived).isCancelled())
return;
return false;
if (rewardReceived.value.isEmpty())
return;
return false;
if (rewardReceived.type.equalsIgnoreCase("credits")) {
int credits = Integer.parseInt(rewardReceived.value);
habbo.giveCredits(credits);
habbo.giveCredits(Integer.parseInt(rewardReceived.value));
completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM);
return true;
} else if (rewardReceived.type.equalsIgnoreCase("diamonds") || rewardReceived.type.equalsIgnoreCase("diamond")) {
habbo.givePoints(5, Integer.parseInt(rewardReceived.value));
completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM);
return true;
} else if (rewardReceived.type.equalsIgnoreCase("pixels")) {
int pixels = Integer.parseInt(rewardReceived.value);
habbo.givePixels(pixels);
habbo.givePixels(Integer.parseInt(rewardReceived.value));
completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM);
return true;
} else if (rewardReceived.type.startsWith("points")) {
int points = Integer.parseInt(rewardReceived.value);
int type = 5;
@@ -342,31 +361,39 @@ public class WiredHandler {
}
habbo.givePoints(type, points);
completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM);
return true;
} else if (rewardReceived.type.equalsIgnoreCase("furni")) {
Item baseItem = Emulator.getGameEnvironment().getItemManager().getItem(Integer.parseInt(rewardReceived.value));
if (baseItem != null) {
HabboItem item = Emulator.getGameEnvironment().getItemManager().createItem(habbo.getHabboInfo().getId(), baseItem, 0, 0, "");
if (baseItem == null)
return false;
HabboItem item = Emulator.getGameEnvironment().getItemManager().createItem(habbo.getHabboInfo().getId(), baseItem, 0, 0, "");
if (item == null)
return false;
if (item != null) {
habbo.getClient().sendResponse(new AddHabboItemComposer(item));
habbo.getClient().getHabbo().getInventory().getItemsComponent().addItem(item);
habbo.getClient().sendResponse(new PurchaseOKComposer(null));
habbo.getClient().sendResponse(new InventoryRefreshComposer());
habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_RECEIVED_ITEM));
}
}
completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM);
return true;
} else if (rewardReceived.type.equalsIgnoreCase("respect")) {
habbo.getHabboStats().respectPointsReceived += Integer.parseInt(rewardReceived.value);
completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM);
return true;
} else if (rewardReceived.type.equalsIgnoreCase("cata")) {
CatalogItem item = Emulator.getGameEnvironment().getCatalogManager().getCatalogItem(Integer.parseInt(rewardReceived.value));
if (item != null) {
if (item == null)
return false;
Emulator.getGameEnvironment().getCatalogManager().purchaseItem(null, item, habbo, 1, "", true);
completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM);
return true;
}
habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_RECEIVED_ITEM));
}
}
}
return false;
}
public static boolean getReward(Habbo habbo, WiredEffectGiveReward wiredBox) {
@@ -433,22 +460,26 @@ public class WiredHandler {
}
if (!found) {
giveReward(habbo, wiredBox, item);
return true;
return giveReward(habbo, wiredBox, item);
}
}
habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_ALL_COLLECTED));
return false;
} else {
int randomNumber = Emulator.getRandom().nextInt(101);
int count = 0;
for (WiredGiveRewardItem item : wiredBox.rewardItems) {
if (randomNumber >= count && randomNumber <= (count + item.probability)) {
giveReward(habbo, wiredBox, item);
return true;
return giveReward(habbo, wiredBox, item);
}
count += item.probability;
}
habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.UNLUCKY_NO_REWARD));
return false;
}
}
}
@@ -466,7 +497,7 @@ public class WiredHandler {
room.getRoomSpecialTypes().getTriggers().forEach(t -> {
if (t == null) return;
if (t.getType() == WiredTriggerType.AT_GIVEN_TIME || t.getType() == WiredTriggerType.PERIODICALLY || t.getType() == WiredTriggerType.PERIODICALLY_LONG) {
if (t.getType() == WiredTriggerType.AT_GIVEN_TIME || t.getType() == WiredTriggerType.PERIODICALLY || t.getType() == WiredTriggerType.PERIODICALLY_LONG || t.getType() == WiredTriggerType.PERIODICALLY_SHORT) {
((WiredTriggerReset) t).resetTimer();
}
});
@@ -15,6 +15,12 @@ public enum WiredTriggerType {
PERIODICALLY_LONG(12),
BOT_REACHED_STF(13),
BOT_REACHED_AVTR(14),
LEAVE_ROOM(16),
PERIODICALLY_SHORT(17),
CLICKS_FURNI(18),
CLICKS_TILE(19),
CLICKS_USER(20),
USER_PERFORMS_ACTION(21),
SAY_COMMAND(0),
IDLES(11),
UNIDLES(11),
@@ -0,0 +1,18 @@
package com.eu.habbo.habbohotel.wired;
public final class WiredUserActionType {
public static final int WAVE = 1;
public static final int BLOW_KISS = 2;
public static final int LAUGH = 3;
public static final int AWAKE = 4;
public static final int RELAX = 5;
public static final int SIT = 6;
public static final int STAND = 7;
public static final int LAY = 8;
public static final int SIGN = 9;
public static final int DANCE = 10;
public static final int THUMB_UP = 11;
private WiredUserActionType() {
}
}
@@ -214,24 +214,16 @@ public final class WiredEngine {
// Initial step for trigger
state.step();
// Activate the trigger box animation
if (stack.triggerItem() instanceof InteractionWiredTrigger) {
InteractionWiredTrigger trigger = (InteractionWiredTrigger) stack.triggerItem();
trigger.activateBox(room, event.getActor().orElse(null), currentTime);
}
debug(room, "Trigger matched: {} at item {} (conditions: {}, effects: {})",
event.getType(),
stack.triggerItem() != null ? stack.triggerItem().getId() : "null",
stack.conditions().size(),
stack.effects().size());
// Activate extras (for their animation)
activateExtras(room, stack.triggerItem(), event.getActor().orElse(null), currentTime);
// Run selectors before conditions so targets are available
List<InteractionWiredEffect> executedSelectors = Collections.emptyList();
if (stack.hasEffects()) {
executeSelectors(stack, ctx, currentTime);
executedSelectors = executeSelectors(stack, ctx);
}
// Evaluate conditions
@@ -253,6 +245,17 @@ public final class WiredEngine {
return false;
}
RoomUnit actor = event.getActor().orElse(null);
// Only show the trigger/selector activation when the stack is actually allowed to continue.
if (stack.triggerItem() instanceof InteractionWiredTrigger) {
InteractionWiredTrigger trigger = (InteractionWiredTrigger) stack.triggerItem();
trigger.activateBox(room, actor, currentTime);
}
activateExtras(room, stack.triggerItem(), actor, currentTime);
finalizeSelectors(executedSelectors, ctx, currentTime);
// Execute effects
if (stack.hasEffects()) {
executeEffects(stack, ctx, currentTime);
@@ -420,9 +423,11 @@ public final class WiredEngine {
/**
* Execute selector effects before conditions so ctx.targets() is populated.
*/
private void executeSelectors(WiredStack stack, WiredContext ctx, long currentTime) {
private List<InteractionWiredEffect> executeSelectors(WiredStack stack, WiredContext ctx) {
List<IWiredEffect> effects = stack.effects();
if (effects.isEmpty()) return;
if (effects.isEmpty()) return Collections.emptyList();
List<InteractionWiredEffect> executedSelectors = new ArrayList<>();
for (IWiredEffect effect : effects) {
if (!effect.isSelector()) continue;
@@ -433,16 +438,29 @@ public final class WiredEngine {
ctx.state().step();
try {
effect.execute(ctx);
if (effect instanceof InteractionWiredEffect) {
InteractionWiredEffect wiredEffect = (InteractionWiredEffect) effect;
wiredEffect.setCooldown(currentTime);
wiredEffect.activateBox(ctx.room(), ctx.actor().orElse(null), currentTime);
executedSelectors.add((InteractionWiredEffect) effect);
}
} catch (Exception e) {
LOGGER.warn("Error executing selector: {}", e.getMessage());
}
}
return executedSelectors;
}
private void finalizeSelectors(List<InteractionWiredEffect> executedSelectors, WiredContext ctx, long currentTime) {
if (executedSelectors == null || executedSelectors.isEmpty()) {
return;
}
Room room = ctx.room();
RoomUnit actor = ctx.actor().orElse(null);
for (InteractionWiredEffect wiredEffect : executedSelectors) {
wiredEffect.setCooldown(currentTime);
wiredEffect.activateBox(room, actor, currentTime);
}
}
/**
@@ -43,6 +43,18 @@ public final class WiredEvent {
/** User walks off furniture */
USER_WALKS_OFF(WiredTriggerType.WALKS_OFF_FURNI),
/** User clicks furniture */
USER_CLICKS_FURNI(WiredTriggerType.CLICKS_FURNI),
/** User clicks invisible click tile furniture */
USER_CLICKS_TILE(WiredTriggerType.CLICKS_TILE),
/** User clicks another user */
USER_CLICKS_USER(WiredTriggerType.CLICKS_USER),
/** User performs an avatar action */
USER_PERFORMS_ACTION(WiredTriggerType.USER_PERFORMS_ACTION),
/** Furniture state is toggled/changed */
FURNI_STATE_CHANGED(WiredTriggerType.STATE_CHANGED),
@@ -55,9 +67,15 @@ public final class WiredEvent {
/** Long timer repeat */
TIMER_REPEAT_LONG(WiredTriggerType.PERIODICALLY_LONG),
/** Short timer repeat */
TIMER_REPEAT_SHORT(WiredTriggerType.PERIODICALLY_SHORT),
/** User enters the room */
USER_ENTERS_ROOM(WiredTriggerType.ENTER_ROOM),
/** User leaves the room */
USER_LEAVES_ROOM(WiredTriggerType.LEAVE_ROOM),
/** Game starts */
GAME_STARTS(WiredTriggerType.GAME_STARTS),
@@ -141,6 +159,8 @@ public final class WiredEvent {
private final boolean triggeredByEffect; // true if triggered by a wired effect (to prevent loops)
private final int callStackDepth; // recursion depth for trigger stacks effect
private final int signalChannel; // channel for signal routing (0-based)
private final int actionId; // user action id for USER_PERFORMS_ACTION
private final int actionParameter; // sign/dance parameter when relevant
private final long createdAtMs;
private WiredEvent(Builder builder) {
@@ -156,6 +176,8 @@ public final class WiredEvent {
this.triggeredByEffect = builder.triggeredByEffect;
this.callStackDepth = builder.callStackDepth;
this.signalChannel = builder.signalChannel;
this.actionId = builder.actionId;
this.actionParameter = builder.actionParameter;
this.createdAtMs = builder.createdAtMs;
}
@@ -258,6 +280,14 @@ public final class WiredEvent {
return signalChannel;
}
public int getActionId() {
return actionId;
}
public int getActionParameter() {
return actionParameter;
}
/**
* Get the timestamp when this event was created.
* @return milliseconds since epoch
@@ -313,6 +343,8 @@ public final class WiredEvent {
private boolean triggeredByEffect;
private int callStackDepth;
private int signalChannel;
private int actionId;
private int actionParameter = -1;
private long createdAtMs = System.currentTimeMillis();
private Builder(Type type, Room room) {
@@ -417,6 +449,16 @@ public final class WiredEvent {
return this;
}
public Builder actionId(int actionId) {
this.actionId = actionId;
return this;
}
public Builder actionParameter(int actionParameter) {
this.actionParameter = actionParameter;
return this;
}
/**
* Set a custom creation timestamp.
* @param createdAtMs milliseconds since epoch
@@ -0,0 +1,74 @@
package com.eu.habbo.habbohotel.wired.core;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
public final class WiredFreezeUtil {
private static final String CACHE_ACTIVE = "wired.freeze.active";
private static final String CACHE_EFFECT_ID = "wired.freeze.effect_id";
private static final String CACHE_CANCEL_ON_TELEPORT = "wired.freeze.cancel_on_teleport";
private WiredFreezeUtil() {
}
public static boolean isFrozen(RoomUnit roomUnit) {
return roomUnit != null && Boolean.TRUE.equals(roomUnit.getCacheable().get(CACHE_ACTIVE));
}
public static void freeze(Room room, RoomUnit roomUnit, int effectId, boolean cancelOnTeleport) {
if (room == null || roomUnit == null || effectId <= 0) {
return;
}
roomUnit.getCacheable().put(CACHE_ACTIVE, true);
roomUnit.getCacheable().put(CACHE_EFFECT_ID, effectId);
roomUnit.getCacheable().put(CACHE_CANCEL_ON_TELEPORT, cancelOnTeleport);
roomUnit.stopWalking();
roomUnit.setCanWalk(false);
roomUnit.statusUpdate(true);
room.giveEffect(roomUnit, effectId, Integer.MAX_VALUE);
room.sendComposer(new RoomUserStatusComposer(roomUnit).compose());
}
public static void unfreeze(Room room, RoomUnit roomUnit) {
if (roomUnit == null) {
return;
}
roomUnit.getCacheable().remove(CACHE_ACTIVE);
roomUnit.getCacheable().remove(CACHE_EFFECT_ID);
roomUnit.getCacheable().remove(CACHE_CANCEL_ON_TELEPORT);
roomUnit.stopWalking();
roomUnit.setCanWalk(true);
roomUnit.statusUpdate(true);
if (room != null) {
room.giveEffect(roomUnit, 0, -1);
room.sendComposer(new RoomUserStatusComposer(roomUnit).compose());
} else {
roomUnit.setEffectId(0, 0);
}
}
public static void onTeleport(Room room, RoomUnit roomUnit) {
if (!isFrozen(roomUnit)) {
return;
}
if (Boolean.TRUE.equals(roomUnit.getCacheable().get(CACHE_CANCEL_ON_TELEPORT))) {
unfreeze(room, roomUnit);
}
}
public static void restoreWalkState(RoomUnit roomUnit) {
if (roomUnit == null) {
return;
}
roomUnit.setCanWalk(!isFrozen(roomUnit));
}
}
@@ -64,6 +64,9 @@ import java.sql.SQLException;
* @see WiredEvents
*/
public final class WiredManager {
private static final String CACHE_LAST_ACTION_ID = "wired.last_user_action.id";
private static final String CACHE_LAST_ACTION_PARAMETER = "wired.last_user_action.parameter";
private static final String CACHE_LAST_ACTION_TIMESTAMP = "wired.last_user_action.timestamp";
private static final Logger LOGGER = LoggerFactory.getLogger(WiredManager.class);
@@ -235,6 +238,58 @@ public final class WiredManager {
return handleEvent(event);
}
/**
* Trigger when a user clicks furniture.
*/
public static boolean triggerUserClicksFurni(Room room, RoomUnit user, HabboItem item) {
if (!isEnabled() || room == null || user == null || item == null) {
return false;
}
WiredEvent event = WiredEvents.userClicksFurni(room, user, item);
return handleEvent(event);
}
/**
* Trigger when a user clicks invisible click tile furniture.
*/
public static boolean triggerUserClicksTile(Room room, RoomUnit user, HabboItem item) {
if (!isEnabled() || room == null || user == null || item == null) {
return false;
}
WiredEvent event = WiredEvents.userClicksTile(room, user, item);
return handleEvent(event);
}
/**
* Trigger when a user clicks another user.
*/
public static boolean triggerUserClicksUser(Room room, RoomUnit clickingUser, RoomUnit clickedUser) {
if (!isEnabled() || room == null || clickingUser == null || clickedUser == null) {
return false;
}
WiredEvent event = WiredEvents.userClicksUser(room, clickingUser, clickedUser);
return handleEvent(event);
}
/**
* Trigger when a user performs an avatar action.
*/
public static boolean triggerUserPerformsAction(Room room, RoomUnit user, int actionId, int actionParameter) {
if (!isEnabled() || room == null || user == null) {
return false;
}
user.getCacheable().put(CACHE_LAST_ACTION_ID, actionId);
user.getCacheable().put(CACHE_LAST_ACTION_PARAMETER, actionParameter);
user.getCacheable().put(CACHE_LAST_ACTION_TIMESTAMP, System.currentTimeMillis());
WiredEvent event = WiredEvents.userPerformsAction(room, user, actionId, actionParameter);
return handleEvent(event);
}
/**
* Trigger when a user says something.
*/
@@ -259,6 +314,18 @@ public final class WiredManager {
return handleEvent(event);
}
/**
* Trigger when a user leaves the room.
*/
public static boolean triggerUserLeavesRoom(Room room, RoomUnit user) {
if (!isEnabled() || room == null || user == null) {
return false;
}
WiredEvent event = WiredEvents.userLeavesRoom(room, user);
return handleEvent(event);
}
/**
* Trigger when furniture state changes.
*/
@@ -295,6 +362,30 @@ public final class WiredManager {
return handleEvent(event);
}
/**
* Trigger a long periodic timer.
*/
public static boolean triggerTimerRepeatLong(Room room, HabboItem timerItem) {
if (!isEnabled() || room == null) {
return false;
}
WiredEvent event = WiredEvents.timerRepeatLong(room, timerItem);
return handleEvent(event);
}
/**
* Trigger a short periodic timer.
*/
public static boolean triggerTimerRepeatShort(Room room, HabboItem timerItem) {
if (!isEnabled() || room == null) {
return false;
}
WiredEvent event = WiredEvents.timerRepeatShort(room, timerItem);
return handleEvent(event);
}
/**
* Trigger game start.
*/
@@ -687,15 +778,7 @@ public final class WiredManager {
});
}
private static void giveReward(Habbo habbo, WiredEffectGiveReward wiredBox, WiredGiveRewardItem reward) {
if (wiredBox.getLimit() > 0)
wiredBox.incrementGiven();
final int wiredId = wiredBox.getId();
final int habboId = habbo.getHabboInfo().getId();
final int rewardId = reward.id;
final int timestamp = Emulator.getIntUnixTimestamp();
private static void persistReward(int wiredId, int habboId, int rewardId, int timestamp) {
Emulator.getThreading().run(() -> {
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
PreparedStatement statement = connection.prepareStatement("INSERT INTO wired_rewards_given (wired_item, user_id, reward_id, timestamp) VALUES ( ?, ?, ?, ?)")) {
@@ -708,41 +791,75 @@ public final class WiredManager {
LOGGER.error("Caught SQL exception", e);
}
});
}
private static void completeReward(Habbo habbo, WiredEffectGiveReward wiredBox, WiredGiveRewardItem reward, int successCode) {
if (wiredBox.getLimit() > 0) {
wiredBox.incrementGiven();
}
persistReward(wiredBox.getId(), habbo.getHabboInfo().getId(), reward.id, Emulator.getIntUnixTimestamp());
habbo.getClient().sendResponse(new WiredRewardAlertComposer(successCode));
}
private static boolean giveReward(Habbo habbo, WiredEffectGiveReward wiredBox, WiredGiveRewardItem reward) {
if (reward.badge) {
UserWiredRewardReceived rewardReceived = new UserWiredRewardReceived(habbo, wiredBox, "badge", reward.data);
if (Emulator.getPluginManager().fireEvent(rewardReceived).isCancelled())
return;
if (Emulator.getPluginManager().fireEvent(rewardReceived).isCancelled()) {
return false;
}
if (rewardReceived.value.isEmpty())
return;
if (rewardReceived.value.isEmpty()) {
return false;
}
if (habbo.getInventory().getBadgesComponent().hasBadge(rewardReceived.value))
return;
if (habbo.getInventory().getBadgesComponent().hasBadge(rewardReceived.value)) {
habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_ALREADY_RECEIVED));
return false;
}
HabboBadge badge = new HabboBadge(0, rewardReceived.value, 0, habbo);
Emulator.getThreading().run(badge);
habbo.getInventory().getBadgesComponent().addBadge(badge);
habbo.getClient().sendResponse(new AddUserBadgeComposer(badge));
habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_RECEIVED_BADGE));
} else {
completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_BADGE);
return true;
}
String[] data = reward.data.split("#");
if (data.length == 2) {
UserWiredRewardReceived rewardReceived = new UserWiredRewardReceived(habbo, wiredBox, data[0], data[1]);
if (Emulator.getPluginManager().fireEvent(rewardReceived).isCancelled())
return;
if (data.length != 2) {
return false;
}
if (rewardReceived.value.isEmpty())
return;
UserWiredRewardReceived rewardReceived = new UserWiredRewardReceived(habbo, wiredBox, data[0], data[1]);
if (Emulator.getPluginManager().fireEvent(rewardReceived).isCancelled()) {
return false;
}
if (rewardReceived.value.isEmpty()) {
return false;
}
if (rewardReceived.type.equalsIgnoreCase("credits")) {
int credits = Integer.parseInt(rewardReceived.value);
habbo.giveCredits(credits);
} else if (rewardReceived.type.equalsIgnoreCase("pixels")) {
int pixels = Integer.parseInt(rewardReceived.value);
habbo.givePixels(pixels);
} else if (rewardReceived.type.startsWith("points")) {
habbo.giveCredits(Integer.parseInt(rewardReceived.value));
completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM);
return true;
}
if (rewardReceived.type.equalsIgnoreCase("diamonds") || rewardReceived.type.equalsIgnoreCase("diamond")) {
habbo.givePoints(5, Integer.parseInt(rewardReceived.value));
completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM);
return true;
}
if (rewardReceived.type.equalsIgnoreCase("pixels")) {
habbo.givePixels(Integer.parseInt(rewardReceived.value));
completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM);
return true;
}
if (rewardReceived.type.startsWith("points")) {
int points = Integer.parseInt(rewardReceived.value);
int type = 5;
@@ -752,31 +869,47 @@ public final class WiredManager {
}
habbo.givePoints(type, points);
} else if (rewardReceived.type.equalsIgnoreCase("furni")) {
Item baseItem = Emulator.getGameEnvironment().getItemManager().getItem(Integer.parseInt(rewardReceived.value));
if (baseItem != null) {
HabboItem item = Emulator.getGameEnvironment().getItemManager().createItem(habbo.getHabboInfo().getId(), baseItem, 0, 0, "");
completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM);
return true;
}
if (rewardReceived.type.equalsIgnoreCase("furni")) {
Item baseItem = Emulator.getGameEnvironment().getItemManager().getItem(Integer.parseInt(rewardReceived.value));
if (baseItem == null) {
return false;
}
HabboItem item = Emulator.getGameEnvironment().getItemManager().createItem(habbo.getHabboInfo().getId(), baseItem, 0, 0, "");
if (item == null) {
return false;
}
if (item != null) {
habbo.getClient().sendResponse(new AddHabboItemComposer(item));
habbo.getClient().getHabbo().getInventory().getItemsComponent().addItem(item);
habbo.getClient().sendResponse(new PurchaseOKComposer(null));
habbo.getClient().sendResponse(new InventoryRefreshComposer());
habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_RECEIVED_ITEM));
completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM);
return true;
}
if (rewardReceived.type.equalsIgnoreCase("respect")) {
habbo.getHabboStats().respectPointsReceived += Integer.parseInt(rewardReceived.value);
completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM);
return true;
}
if (rewardReceived.type.equalsIgnoreCase("cata")) {
CatalogItem item = Emulator.getGameEnvironment().getCatalogManager().getCatalogItem(Integer.parseInt(rewardReceived.value));
if (item == null) {
return false;
}
}
} else if (rewardReceived.type.equalsIgnoreCase("respect")) {
habbo.getHabboStats().respectPointsReceived += Integer.parseInt(rewardReceived.value);
} else if (rewardReceived.type.equalsIgnoreCase("cata")) {
CatalogItem item = Emulator.getGameEnvironment().getCatalogManager().getCatalogItem(Integer.parseInt(rewardReceived.value));
if (item != null) {
Emulator.getGameEnvironment().getCatalogManager().purchaseItem(null, item, habbo, 1, "", true);
completeReward(habbo, wiredBox, reward, WiredRewardAlertComposer.REWARD_RECEIVED_ITEM);
return true;
}
habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_RECEIVED_ITEM));
}
}
}
return false;
}
public static boolean getReward(Habbo habbo, WiredEffectGiveReward wiredBox) {
@@ -843,22 +976,26 @@ public final class WiredManager {
}
if (!found) {
giveReward(habbo, wiredBox, item);
return true;
return giveReward(habbo, wiredBox, item);
}
}
habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.REWARD_ALL_COLLECTED));
return false;
} else {
int randomNumber = Emulator.getRandom().nextInt(101);
int count = 0;
for (WiredGiveRewardItem item : wiredBox.getRewardItems()) {
if (randomNumber >= count && randomNumber <= (count + item.probability)) {
giveReward(habbo, wiredBox, item);
return true;
return giveReward(habbo, wiredBox, item);
}
count += item.probability;
}
habbo.getClient().sendResponse(new WiredRewardAlertComposer(WiredRewardAlertComposer.UNLUCKY_NO_REWARD));
return false;
}
}
}
@@ -3,6 +3,7 @@ package com.eu.habbo.habbohotel.wired.highscores;
import com.eu.habbo.Emulator;
import com.eu.habbo.plugin.EventHandler;
import com.eu.habbo.plugin.events.emulator.EmulatorLoadedEvent;
import com.eu.habbo.util.HotelDateTimeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -11,9 +12,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.util.*;
@@ -31,8 +30,6 @@ public class WiredHighscoreManager {
private final static DayOfWeek firstDayOfWeek = WeekFields.of(Locale.of(locale, country)).getFirstDayOfWeek();
private final static DayOfWeek lastDayOfWeek = DayOfWeek.of(((firstDayOfWeek.getValue() + 5) % DayOfWeek.values().length) + 1);
private final static ZoneId zoneId = ZoneId.systemDefault();
public static ScheduledFuture<?> midnightUpdater = null;
public void load() {
@@ -183,26 +180,26 @@ public class WiredHighscoreManager {
}
private long getTodayStartTimestamp() {
return LocalDateTime.now().with(LocalTime.MIDNIGHT).atZone(zoneId).toEpochSecond();
return HotelDateTimeUtil.toEpochSecond(HotelDateTimeUtil.localDateTimeNow().with(LocalTime.MIDNIGHT));
}
private long getTodayEndTimestamp() {
return LocalDateTime.now().with(LocalTime.MIDNIGHT).plusDays(1).plusSeconds(-1).atZone(zoneId).toEpochSecond();
return HotelDateTimeUtil.toEpochSecond(HotelDateTimeUtil.localDateTimeNow().with(LocalTime.MIDNIGHT).plusDays(1).plusSeconds(-1));
}
private long getWeekStartTimestamp() {
return LocalDateTime.now().with(LocalTime.MIDNIGHT).with(TemporalAdjusters.previousOrSame(firstDayOfWeek)).atZone(zoneId).toEpochSecond();
return HotelDateTimeUtil.toEpochSecond(HotelDateTimeUtil.localDateTimeNow().with(LocalTime.MIDNIGHT).with(TemporalAdjusters.previousOrSame(firstDayOfWeek)));
}
private long getWeekEndTimestamp() {
return LocalDateTime.now().with(LocalTime.MIDNIGHT).plusDays(1).plusSeconds(-1).with(TemporalAdjusters.nextOrSame(lastDayOfWeek)).atZone(zoneId).toEpochSecond();
return HotelDateTimeUtil.toEpochSecond(HotelDateTimeUtil.localDateTimeNow().with(LocalTime.MIDNIGHT).plusDays(1).plusSeconds(-1).with(TemporalAdjusters.nextOrSame(lastDayOfWeek)));
}
private long getMonthStartTimestamp() {
return LocalDateTime.now().with(LocalTime.MIDNIGHT).with(TemporalAdjusters.firstDayOfMonth()).atZone(zoneId).toEpochSecond();
return HotelDateTimeUtil.toEpochSecond(HotelDateTimeUtil.localDateTimeNow().with(LocalTime.MIDNIGHT).with(TemporalAdjusters.firstDayOfMonth()));
}
private long getMonthEndTimestamp() {
return LocalDateTime.now().with(LocalTime.MIDNIGHT).plusDays(1).plusSeconds(-1).with(TemporalAdjusters.lastDayOfMonth()).atZone(zoneId).toEpochSecond();
return HotelDateTimeUtil.toEpochSecond(HotelDateTimeUtil.localDateTimeNow().with(LocalTime.MIDNIGHT).plusDays(1).plusSeconds(-1).with(TemporalAdjusters.lastDayOfMonth()));
}
}
@@ -4,11 +4,10 @@ import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredHighscore;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.util.HotelDateTimeUtil;
import gnu.trove.set.hash.THashSet;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.List;
public class WiredHighscoreMidnightUpdater implements Runnable {
@@ -30,6 +29,7 @@ public class WiredHighscoreMidnightUpdater implements Runnable {
}
public static int getNextUpdaterRun() {
return Math.toIntExact(LocalDateTime.now().with(LocalTime.MIDNIGHT).plusDays(1).plusSeconds(-1).atZone(ZoneId.systemDefault()).toEpochSecond() - Emulator.getIntUnixTimestamp()) + 5;
long nextRunTimestamp = HotelDateTimeUtil.toEpochSecond(HotelDateTimeUtil.localDateTimeNow().with(LocalTime.MIDNIGHT).plusDays(1).plusSeconds(-1));
return Math.toIntExact(nextRunTimestamp - Emulator.getIntUnixTimestamp()) + 5;
}
}
@@ -67,6 +67,70 @@ public final class WiredEvents {
.build();
}
/**
* Create an event for when a user clicks furniture.
* @param room the room
* @param user the clicking user
* @param item the clicked furniture
* @return the event
*/
public static WiredEvent userClicksFurni(Room room, RoomUnit user, HabboItem item) {
RoomTile tile = room.getLayout().getTile(item.getX(), item.getY());
return WiredEvent.builder(WiredEvent.Type.USER_CLICKS_FURNI, room)
.actor(user)
.sourceItem(item)
.tile(tile)
.build();
}
/**
* Create an event for when a user clicks invisible click tile furniture.
* @param room the room
* @param user the clicking user
* @param item the clicked furniture
* @return the event
*/
public static WiredEvent userClicksTile(Room room, RoomUnit user, HabboItem item) {
RoomTile tile = room.getLayout().getTile(item.getX(), item.getY());
return WiredEvent.builder(WiredEvent.Type.USER_CLICKS_TILE, room)
.actor(user)
.sourceItem(item)
.tile(tile)
.build();
}
/**
* Create an event for when a user clicks another user.
* @param room the room
* @param clickingUser the user performing the click
* @param clickedUser the user who was clicked
* @return the event
*/
public static WiredEvent userClicksUser(Room room, RoomUnit clickingUser, RoomUnit clickedUser) {
return WiredEvent.builder(WiredEvent.Type.USER_CLICKS_USER, room)
.actor(clickedUser)
.targetUnit(clickingUser)
.tile(clickedUser.getCurrentLocation())
.build();
}
/**
* Create an event for when a user performs an avatar action.
* @param room the room
* @param user the acting user
* @param actionId the wired action id
* @param actionParameter sign/dance parameter, or -1 when unused
* @return the event
*/
public static WiredEvent userPerformsAction(Room room, RoomUnit user, int actionId, int actionParameter) {
return WiredEvent.builder(WiredEvent.Type.USER_PERFORMS_ACTION, room)
.actor(user)
.tile(user.getCurrentLocation())
.actionId(actionId)
.actionParameter(actionParameter)
.build();
}
/**
* Create an event for when a user enters the room.
* @param room the room
@@ -80,6 +144,19 @@ public final class WiredEvents {
.build();
}
/**
* Create an event for when a user leaves the room.
* @param room the room
* @param user the user who left
* @return the event
*/
public static WiredEvent userLeavesRoom(Room room, RoomUnit user) {
return WiredEvent.builder(WiredEvent.Type.USER_LEAVES_ROOM, room)
.actor(user)
.tile(user.getCurrentLocation())
.build();
}
// ========== User Interaction Events ==========
/**
@@ -153,6 +230,18 @@ public final class WiredEvents {
.build();
}
/**
* Create an event for a short periodic timer.
* @param room the room
* @param timerItem the timer furniture
* @return the event
*/
public static WiredEvent timerRepeatShort(Room room, HabboItem timerItem) {
return WiredEvent.builder(WiredEvent.Type.TIMER_REPEAT_SHORT, room)
.sourceItem(timerItem)
.build();
}
// ========== Game Events ==========
/**
@@ -394,6 +394,8 @@ public class PacketManager {
this.registerHandler(Incoming.RoomPlacePaintEvent, RoomPlacePaintEvent.class);
this.registerHandler(Incoming.RoomUserStartTypingEvent, RoomUserStartTypingEvent.class);
this.registerHandler(Incoming.RoomUserStopTypingEvent, RoomUserStopTypingEvent.class);
this.registerHandler(Incoming.ClickFurniEvent, ClickFurniEvent.class);
this.registerHandler(Incoming.ClickUserEvent, ClickUserEvent.class);
this.registerHandler(Incoming.ToggleFloorItemEvent, ToggleFloorItemEvent.class);
this.registerHandler(Incoming.ToggleWallItemEvent, ToggleWallItemEvent.class);
this.registerHandler(Incoming.RoomBackgroundEvent, RoomBackgroundEvent.class);
@@ -205,6 +205,7 @@ public class Incoming {
public static final int RequestRoomDataEvent = 2230;
public static final int RequestRoomHeightmapEvent = 2300;
public static final int RequestGuildFurniWidgetEvent = 2651;
public static final int ClickFurniEvent = 6002;
public static final int RequestOwnItemsEvent = 2105;
public static final int RequestReportRoomEvent = 3267;
public static final int ReportEvent = 1691;
@@ -379,7 +380,7 @@ public class Incoming {
public static final int UNKNOWN_SNOWSTORM_6000 = 6000;
public static final int UNKNOWN_SNOWSTORM_6001 = 6001;
public static final int UNKNOWN_SNOWSTORM_6002 = 6002;
// public static final int UNKNOWN_SNOWSTORM_6002 = 6002;
public static final int UNKNOWN_SNOWSTORM_6003 = 6003;
public static final int UNKNOWN_SNOWSTORM_6004 = 6004;
public static final int UNKNOWN_SNOWSTORM_6005 = 6005;
@@ -407,6 +408,7 @@ public class Incoming {
// CUSTOM
public static final int UpdateFurniturePositionEvent = 10019;
public static final int ClickUserEvent = 10020;
public static final int RequestInventoryPetDelete = 10030;
public static final int RequestInventoryBadgeDelete = 10031;
@@ -2,10 +2,13 @@ package com.eu.habbo.messages.incoming.handshake;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.messenger.Messenger;
import com.eu.habbo.habbohotel.gameclients.GameClient;
import com.eu.habbo.habbohotel.gameclients.SessionResumeManager;
import com.eu.habbo.habbohotel.modtool.ModToolSanctionItem;
import com.eu.habbo.habbohotel.modtool.ModToolSanctions;
import com.eu.habbo.habbohotel.navigation.NavigatorSavedSearch;
import com.eu.habbo.habbohotel.permissions.Permission;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomManager;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboManager;
@@ -81,8 +84,70 @@ public class SecureLoginEvent extends MessageHandler {
}
if (this.client.getHabbo() == null) {
Habbo habbo = Emulator.getGameEnvironment().getHabboManager().loadHabbo(sso);
// Store SSO ticket on client for grace period tracking
this.client.setSsoTicket(sso);
// Race condition fix: if the old WebSocket connection is still alive on the
// server when the client reconnects, the SSO ticket won't be in the DB yet
// (it was cleared on first login, and parkHabbo hasn't run because the old
// channel hasn't closed). Find the old client by SSO ticket and force-dispose
// it, which parks the habbo and restores the ticket to the DB.
GameClient existingClient = Emulator.getGameServer().getGameClientManager().findClientBySsoTicket(sso);
if (existingClient != null && existingClient != this.client) {
LOGGER.info("[SessionResume] Found existing client with same SSO ticket — disposing old connection to trigger parking");
Emulator.getGameServer().getGameClientManager().disposeClient(existingClient);
}
// First, look up the user ID to check for ghost sessions
int lookupUserId = 0;
try (java.sql.Connection conn = Emulator.getDatabase().getDataSource().getConnection();
java.sql.PreparedStatement stmt = conn.prepareStatement("SELECT id FROM users WHERE auth_ticket = ? LIMIT 1")) {
stmt.setString(1, sso);
try (java.sql.ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
lookupUserId = rs.getInt("id");
}
}
} catch (Exception e) {
LOGGER.error("Caught exception looking up user for session resume", e);
}
// Check if this user has a ghost session (disconnected within grace period)
Habbo habbo = null;
boolean isSessionResume = false;
if (lookupUserId > 0) {
habbo = SessionResumeManager.getInstance().resumeSession(lookupUserId);
}
if (habbo != null) {
// Session resume reattach the existing Habbo to the new client
isSessionResume = true;
LOGGER.info("[SessionResume] Resuming session for {} (id={})",
habbo.getHabboInfo().getUsername(), habbo.getHabboInfo().getId());
habbo.setClient(this.client);
this.client.setHabbo(habbo);
this.client.setMachineId(habbo.getHabboInfo().getMachineID());
// Clear the SSO ticket now that session is resumed (prevent reuse)
if (!Emulator.debugging) {
try (java.sql.Connection conn = Emulator.getDatabase().getDataSource().getConnection();
java.sql.PreparedStatement stmt = conn.prepareStatement("UPDATE users SET auth_ticket = ? WHERE id = ? LIMIT 1")) {
stmt.setString(1, "");
stmt.setInt(2, habbo.getHabboInfo().getId());
stmt.execute();
} catch (Exception e) {
LOGGER.error("Failed to clear SSO ticket after session resume", e);
}
}
} else {
// Normal login load from database
habbo = Emulator.getGameEnvironment().getHabboManager().loadHabbo(sso);
}
if (habbo != null) {
if (!isSessionResume) {
try {
habbo.setClient(this.client);
this.client.setHabbo(habbo);
@@ -107,6 +172,7 @@ public class SecureLoginEvent extends MessageHandler {
Emulator.getGameServer().getGameClientManager().disposeClient(this.client);
return;
}
}
if(ClothingValidationManager.VALIDATE_ON_LOGIN) {
String validated = ClothingValidationManager.validateLook(this.client.getHabbo());
@@ -121,7 +187,18 @@ public class SecureLoginEvent extends MessageHandler {
int roomIdToEnter = 0;
if (!this.client.getHabbo().getHabboStats().nux || Emulator.getConfig().getBoolean("retro.style.homeroom") && this.client.getHabbo().getHabboInfo().getHomeRoom() != 0)
if (isSessionResume) {
// On session resume, DON'T set roomIdToEnter. The client keeps its
// existing room view alive and the habbo is already in the room on
// the server. Setting roomIdToEnter = 0 prevents UserHomeRoomComposer
// from triggering a full room re-entry on the client (which would
// tear down and rebuild the room view).
Room currentRoom = habbo.getHabboInfo().getCurrentRoom();
if (currentRoom != null) {
LOGGER.info("[SessionResume] {} is still in room {} — client will resume in-place",
habbo.getHabboInfo().getUsername(), currentRoom.getId());
}
} else if (!this.client.getHabbo().getHabboStats().nux || Emulator.getConfig().getBoolean("retro.style.homeroom") && this.client.getHabbo().getHabboInfo().getHomeRoom() != 0)
roomIdToEnter = this.client.getHabbo().getHabboInfo().getHomeRoom();
else if (!this.client.getHabbo().getHabboStats().nux || Emulator.getConfig().getBoolean("retro.style.homeroom") && RoomManager.HOME_ROOM_ID > 0)
roomIdToEnter = RoomManager.HOME_ROOM_ID;
@@ -189,6 +266,8 @@ public class SecureLoginEvent extends MessageHandler {
}
}
// Skip login-only events on session resume (welcome alerts, login events, etc.)
if (!isSessionResume) {
UserLoginEvent userLoginEvent = new UserLoginEvent(habbo, this.client.getHabbo().getHabboInfo().getIpLogin());
Emulator.getPluginManager().fireEvent(userLoginEvent);
@@ -226,6 +305,7 @@ public class SecureLoginEvent extends MessageHandler {
this.client.sendResponse(new NewNavigatorSavedSearchesComposer(this.client.getHabbo().getHabboInfo().getSavedSearches()));
}
}
} else {
Emulator.getGameServer().getGameClientManager().disposeClient(this.client);
LOGGER.warn("Someone tried to login with a non-existing SSO token! Closed connection...");
@@ -3,9 +3,9 @@ package com.eu.habbo.messages.incoming.hotelview;
import com.eu.habbo.Emulator;
import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.hotelview.HotelViewSecondsUntilComposer;
import com.eu.habbo.util.HotelDateTimeUtil;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
@@ -18,7 +18,7 @@ public class HotelViewRequestSecondsUntilEvent extends MessageHandler {
try {
LocalDateTime dt = LocalDateTime.parse(date, formatter);
int secondsUntil = Math.max(0, (int) dt.atZone(ZoneId.systemDefault()).toEpochSecond() - Emulator.getIntUnixTimestamp());
int secondsUntil = Math.max(0, (int) HotelDateTimeUtil.toEpochSecond(dt) - Emulator.getIntUnixTimestamp());
this.client.sendResponse(new HotelViewSecondsUntilComposer(date, secondsUntil));
} catch (DateTimeParseException ignored) {
}
@@ -2,15 +2,35 @@ package com.eu.habbo.messages.incoming.rooms;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomTile;
import com.eu.habbo.messages.incoming.MessageHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RequestRoomLoadEvent extends MessageHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(RequestRoomLoadEvent.class);
@Override
public void handle() throws Exception {
int roomId = this.packet.readInt();
String password = this.packet.readString();
// Optional spawn coordinates from the client (for future reconnection support).
int spawnX = -1;
int spawnY = -1;
try {
int remaining = this.packet.getBuffer().readableBytes();
if (remaining >= 8) {
spawnX = this.packet.readInt();
spawnY = this.packet.readInt();
}
} catch (Exception e) {
spawnX = -1;
spawnY = -1;
}
// Reset stale loadingRoom if timestamp has expired (indicates failed/stuck load)
if (this.client.getHabbo().getHabboInfo().getLoadingRoom() != 0
&& this.client.getHabbo().getHabboStats().roomEnterTimestamp + 5000 < System.currentTimeMillis()) {
@@ -30,6 +50,18 @@ public class RequestRoomLoadEvent extends MessageHandler {
Room room = this.client.getHabbo().getHabboInfo().getCurrentRoom();
if (room != null) {
// If re-entering the same room (session resume / reconnect), capture
// the user's current position before removal so we can respawn there.
if (room.getId() == roomId && spawnX < 0 && spawnY < 0
&& this.client.getHabbo().getRoomUnit() != null
&& this.client.getHabbo().getRoomUnit().getCurrentLocation() != null) {
RoomTile currentLoc = this.client.getHabbo().getRoomUnit().getCurrentLocation();
spawnX = currentLoc.x;
spawnY = currentLoc.y;
LOGGER.info("[RequestRoomLoadEvent] Re-entering same room {} — preserving position ({}, {})",
roomId, spawnX, spawnY);
}
Emulator.getGameEnvironment().getRoomManager().logExit(this.client.getHabbo());
room.removeHabbo(this.client.getHabbo(), true);
@@ -41,7 +73,28 @@ public class RequestRoomLoadEvent extends MessageHandler {
this.client.getHabbo().getRoomUnit().isTeleporting = false;
}
Emulator.getGameEnvironment().getRoomManager().enterRoom(this.client.getHabbo(), roomId, password);
// Resolve spawn tile from coordinates (either from client or from saved position above)
RoomTile spawnTile = null;
if (spawnX >= 0 && spawnY >= 0) {
Room targetRoom = Emulator.getGameEnvironment().getRoomManager().getRoom(roomId);
if (targetRoom == null) {
targetRoom = Emulator.getGameEnvironment().getRoomManager().loadRoom(roomId);
}
if (targetRoom != null && targetRoom.getLayout() != null) {
RoomTile tile = targetRoom.getLayout().getTile((short) spawnX, (short) spawnY);
if (tile != null && tile.isWalkable()) {
spawnTile = tile;
}
}
}
boolean isReconnect = spawnTile != null;
LOGGER.debug("[RequestRoomLoadEvent] Entering room {} (spawnTile={}, isReconnect={})",
roomId,
spawnTile != null ? "(" + spawnTile.x + "," + spawnTile.y + ")" : "door",
isReconnect);
Emulator.getGameEnvironment().getRoomManager().enterRoom(this.client.getHabbo(), roomId, password, false, spawnTile, isReconnect);
}
}
}
@@ -0,0 +1,43 @@
package com.eu.habbo.messages.incoming.rooms.items;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.messages.incoming.MessageHandler;
public class ClickFurniEvent extends MessageHandler {
private static final String CLICK_TILE_INTERACTION = "room_invisible_click_tile";
@Override
public void handle() throws Exception {
Room room = this.client.getHabbo().getHabboInfo().getCurrentRoom();
if (room == null) {
return;
}
int itemId = Math.abs(this.packet.readInt());
this.packet.readInt();
HabboItem item = room.getHabboItem(itemId);
if (item == null) {
return;
}
WiredManager.triggerUserClicksFurni(room, this.client.getHabbo().getRoomUnit(), item);
if (isClickTileItem(item)) {
WiredManager.triggerUserClicksTile(room, this.client.getHabbo().getRoomUnit(), item);
}
}
private boolean isClickTileItem(HabboItem item) {
if (item == null || item.getBaseItem() == null || item.getBaseItem().getInteractionType() == null) {
return false;
}
String interaction = item.getBaseItem().getInteractionType().getName();
return interaction != null && interaction.equalsIgnoreCase(CLICK_TILE_INTERACTION);
}
}
@@ -0,0 +1,33 @@
package com.eu.habbo.messages.incoming.rooms.users;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.messages.incoming.MessageHandler;
public class ClickUserEvent extends MessageHandler {
@Override
public void handle() throws Exception {
Room room = this.client.getHabbo().getHabboInfo().getCurrentRoom();
if (room == null) {
return;
}
RoomUnit clickingUser = this.client.getHabbo().getRoomUnit();
if (clickingUser == null) {
return;
}
int roomUnitId = this.packet.readInt();
Habbo clickedHabbo = room.getHabboByRoomUnitId(roomUnitId);
if (clickedHabbo == null || clickedHabbo.getRoomUnit() == null) {
return;
}
WiredManager.triggerUserClicksUser(room, clickingUser, clickedHabbo.getRoomUnit());
}
}
@@ -4,6 +4,8 @@ import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUserAction;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.wired.WiredUserActionType;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserActionComposer;
import com.eu.habbo.plugin.events.users.UserIdleEvent;
@@ -26,6 +28,7 @@ public class RoomUserActionEvent extends MessageHandler {
}
int action = this.packet.readInt();
int wiredAction = 0;
if (action == 5) {
UserIdleEvent event = new UserIdleEvent(this.client.getHabbo(), UserIdleEvent.IdleReason.ACTION, true);
@@ -34,8 +37,10 @@ public class RoomUserActionEvent extends MessageHandler {
if (!event.isCancelled()) {
if (event.idle) {
room.idle(habbo);
wiredAction = WiredUserActionType.RELAX;
} else {
room.unIdle(habbo);
wiredAction = WiredUserActionType.AWAKE;
}
}
} else {
@@ -51,6 +56,29 @@ public class RoomUserActionEvent extends MessageHandler {
}
room.sendComposer(new RoomUserActionComposer(habbo.getRoomUnit(), RoomUserAction.fromValue(action)).compose());
if (wiredAction == 0) {
switch (action) {
case 1:
wiredAction = WiredUserActionType.WAVE;
break;
case 2:
wiredAction = WiredUserActionType.BLOW_KISS;
break;
case 3:
wiredAction = WiredUserActionType.LAUGH;
break;
case 7:
wiredAction = WiredUserActionType.THUMB_UP;
break;
default:
break;
}
}
if (wiredAction != 0) {
WiredManager.triggerUserPerformsAction(room, habbo.getRoomUnit(), wiredAction, -1);
}
}
}
}
@@ -3,8 +3,9 @@ package com.eu.habbo.messages.incoming.rooms.users;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.users.DanceType;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.wired.WiredUserActionType;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDanceComposer;
import com.eu.habbo.plugin.events.users.UserIdleEvent;
public class RoomUserDanceEvent extends MessageHandler {
@@ -14,7 +15,7 @@ public class RoomUserDanceEvent extends MessageHandler {
return;
int danceId = this.packet.readInt();
if (danceId >= 0 && danceId <= 5) {
if (danceId >= 0 && danceId <= 4) {
if (this.client.getHabbo().getRoomUnit().isInRoom()) {
Habbo habbo = this.client.getHabbo();
@@ -29,8 +30,6 @@ public class RoomUserDanceEvent extends MessageHandler {
}
}
habbo.getRoomUnit().setDanceType(DanceType.values()[danceId]);
UserIdleEvent event = new UserIdleEvent(this.client.getHabbo(), UserIdleEvent.IdleReason.DANCE, false);
Emulator.getPluginManager().fireEvent(event);
@@ -40,7 +39,11 @@ public class RoomUserDanceEvent extends MessageHandler {
}
}
this.client.getHabbo().getHabboInfo().getCurrentRoom().sendComposer(new RoomUserDanceComposer(habbo.getRoomUnit()).compose());
this.client.getHabbo().getHabboInfo().getCurrentRoom().dance(habbo, DanceType.values()[danceId]);
if (danceId > 0) {
WiredManager.triggerUserPerformsAction(this.client.getHabbo().getHabboInfo().getCurrentRoom(), habbo.getRoomUnit(), WiredUserActionType.DANCE, danceId);
}
}
}
}
@@ -5,6 +5,8 @@ import com.eu.habbo.habbohotel.items.interactions.InteractionVoteCounter;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredUserActionType;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.plugin.events.users.UserSignEvent;
@@ -22,6 +24,7 @@ public class RoomUserSignEvent extends MessageHandler {
if (!Emulator.getPluginManager().fireEvent(event).isCancelled()) {
this.client.getHabbo().getRoomUnit().setStatus(RoomUnitStatus.SIGN, event.sign + "");
this.client.getHabbo().getHabboInfo().getCurrentRoom().unIdle(this.client.getHabbo());
WiredManager.triggerUserPerformsAction(room, this.client.getHabbo().getRoomUnit(), WiredUserActionType.SIGN, event.sign);
if(signId <= 10) {
@@ -7,11 +7,18 @@ import com.eu.habbo.plugin.events.users.UserIdleEvent;
public class RoomUserSitEvent extends MessageHandler {
@Override
public void handle() throws Exception {
int posture = this.packet.readInt();
if (this.client.getHabbo().getHabboInfo().getCurrentRoom() != null) {
if (this.client.getHabbo().getRoomUnit().isWalking()) {
this.client.getHabbo().getRoomUnit().stopWalking();
}
if (posture == 0) {
this.client.getHabbo().getHabboInfo().getCurrentRoom().makeStand(this.client.getHabbo());
} else {
this.client.getHabbo().getHabboInfo().getCurrentRoom().makeSit(this.client.getHabbo());
}
UserIdleEvent event = new UserIdleEvent(this.client.getHabbo(), UserIdleEvent.IdleReason.WALKED, false);
Emulator.getPluginManager().fireEvent(event);
@@ -10,6 +10,7 @@ import com.eu.habbo.habbohotel.rooms.BedProfile;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboInfo;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUnitOnRollerComposer;
import com.eu.habbo.plugin.events.users.UserIdleEvent;
@@ -46,7 +47,7 @@ public class RoomUserWalkEvent extends MessageHandler {
Room room = habboInfo.getCurrentRoom();
try {
if (roomUnit != null && roomUnit.isInRoom() && roomUnit.canWalk()) {
if (roomUnit != null && roomUnit.isInRoom() && roomUnit.canWalk() && !WiredFreezeUtil.isFrozen(roomUnit)) {
if (roomUnit.cmdTeleport) {
handleTeleport(room, (short) x, (short) y, roomUnit, habboInfo);
return;
@@ -34,6 +34,7 @@ public class WiredTriggerSaveDataEvent extends MessageHandler {
if (saveMethod.get().getParameterTypes()[0] == WiredSettings.class) {
WiredSettings settings = InteractionWired.readSettings(this.packet, false);
try {
if (trigger.saveData(settings)) {
this.client.sendResponse(new WiredSavedComposer());
@@ -46,6 +47,9 @@ public class WiredTriggerSaveDataEvent extends MessageHandler {
} else {
this.client.sendResponse(new UpdateFailedComposer("There was an error while saving that trigger"));
}
} catch (WiredTriggerSaveException e) {
this.client.sendResponse(new UpdateFailedComposer(e.getMessage()));
}
} else {
if ((boolean) saveMethod.get().invoke(trigger, this.packet)) {
this.client.sendResponse(new WiredSavedComposer());
@@ -0,0 +1,7 @@
package com.eu.habbo.messages.incoming.wired;
public class WiredTriggerSaveException extends RuntimeException {
public WiredTriggerSaveException(String message) {
super(message);
}
}
@@ -504,6 +504,7 @@ public class Outgoing {
public final static int WiredOpenComposer = 1830;
public final static int UnknownCatalogPageOfferComposer = 1889;
public final static int NuxAlertComposer = 2023;
public final static int InClientLinkComposer = 2023;
public final static int HotelViewExpiringCatalogPageCommposer = 2515;
public final static int UnknownHabboWayQuizComposer = 2772;
public final static int PetLevelUpdatedComposer = 2824;
@@ -0,0 +1,20 @@
package com.eu.habbo.messages.outgoing.users;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.outgoing.MessageComposer;
import com.eu.habbo.messages.outgoing.Outgoing;
public class InClientLinkComposer extends MessageComposer {
private final String link;
public InClientLinkComposer(String link) {
this.link = link;
}
@Override
protected ServerMessage composeInternal() {
this.response.init(Outgoing.InClientLinkComposer);
this.response.appendString(this.link);
return this.response;
}
}
@@ -8,7 +8,7 @@ public class MutedWhisperComposer extends MessageComposer {
private final int seconds;
public MutedWhisperComposer(int seconds) {
this.seconds = seconds;
this.seconds = Math.max(0, seconds);
}
@Override
@@ -5,6 +5,7 @@ import com.eu.habbo.habbohotel.rooms.RoomTile;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,6 +39,8 @@ public class RoomUnitTeleport implements Runnable {
return;
}
WiredFreezeUtil.onTeleport(this.room, this.roomUnit);
RoomTile lastLocation = this.roomUnit.getCurrentLocation();
RoomTile newLocation = this.room.getLayout().getTile((short) this.x, (short) this.y);
@@ -60,6 +63,7 @@ public class RoomUnitTeleport implements Runnable {
//this.room.sendComposer(teleportMessage);
this.roomUnit.statusUpdate(true);
roomUnit.isWiredTeleporting = false;
WiredFreezeUtil.restoreWalkState(this.roomUnit);
this.room.updateHabbosAt(newLocation.x, newLocation.y);
this.room.updateBotsAt(newLocation.x, newLocation.y);
@@ -5,6 +5,7 @@ import com.eu.habbo.habbohotel.gameclients.GameClient;
import com.eu.habbo.habbohotel.items.interactions.InteractionTeleportTile;
import com.eu.habbo.habbohotel.rooms.*;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
import com.eu.habbo.threading.runnables.HabboItemNewState;
import com.eu.habbo.threading.runnables.RoomUnitWalkToLocation;
@@ -46,6 +47,7 @@ class TeleportActionFive implements Runnable {
List<Runnable> onSuccess = new ArrayList<Runnable>();
onSuccess.add(() -> {
unit.setCanLeaveRoomByDoor(true);
WiredFreezeUtil.restoreWalkState(unit);
Emulator.getThreading().run(() -> {
unit.isLeavingTeleporter = false;
@@ -57,6 +59,8 @@ class TeleportActionFive implements Runnable {
unit.statusUpdate(true);
unit.isLeavingTeleporter = true;
Emulator.getThreading().run(new RoomUnitWalkToLocation(unit, tile, room, onSuccess, onSuccess));
} else {
WiredFreezeUtil.restoreWalkState(unit);
}
this.currentTeleport.setExtradata("1");
@@ -4,6 +4,7 @@ import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.gameclients.GameClient;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
class TeleportActionFour implements Runnable {
private final HabboItem currentTeleport;
@@ -21,7 +22,7 @@ class TeleportActionFour implements Runnable {
if (this.client.getHabbo().getHabboInfo().getCurrentRoom() != this.room) {
this.client.getHabbo().getHabboInfo().setLoadingRoom(0);
this.client.getHabbo().getRoomUnit().isTeleporting = false;
this.client.getHabbo().getRoomUnit().setCanWalk(true);
WiredFreezeUtil.restoreWalkState(this.client.getHabbo().getRoomUnit());
this.currentTeleport.setExtradata("0");
this.room.updateItem(this.currentTeleport);
return;
@@ -7,6 +7,7 @@ import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
import com.eu.habbo.habbohotel.rooms.RoomUserRotation;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
public class TeleportActionOne implements Runnable {
@@ -25,7 +26,7 @@ public class TeleportActionOne implements Runnable {
if (this.client.getHabbo().getHabboInfo().getCurrentRoom() != this.room) {
this.client.getHabbo().getHabboInfo().setLoadingRoom(0);
this.client.getHabbo().getRoomUnit().isTeleporting = false;
this.client.getHabbo().getRoomUnit().setCanWalk(true);
WiredFreezeUtil.restoreWalkState(this.client.getHabbo().getRoomUnit());
return;
}
@@ -9,6 +9,7 @@ import com.eu.habbo.habbohotel.rooms.RoomTile;
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
import com.eu.habbo.habbohotel.rooms.RoomUserRotation;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
class TeleportActionThree implements Runnable {
private final HabboItem currentTeleport;
@@ -26,7 +27,7 @@ class TeleportActionThree implements Runnable {
if (this.client.getHabbo().getHabboInfo().getCurrentRoom() != this.room) {
this.client.getHabbo().getHabboInfo().setLoadingRoom(0);
this.client.getHabbo().getRoomUnit().isTeleporting = false;
this.client.getHabbo().getRoomUnit().setCanWalk(true);
WiredFreezeUtil.restoreWalkState(this.client.getHabbo().getRoomUnit());
return;
}
@@ -7,6 +7,7 @@ import com.eu.habbo.habbohotel.items.interactions.InteractionTeleportTile;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
import com.eu.habbo.threading.runnables.HabboItemNewState;
import org.slf4j.Logger;
@@ -41,7 +42,7 @@ class TeleportActionTwo implements Runnable {
if (this.client.getHabbo().getHabboInfo().getCurrentRoom() != this.room) {
this.client.getHabbo().getHabboInfo().setLoadingRoom(0);
this.client.getHabbo().getRoomUnit().isTeleporting = false;
this.client.getHabbo().getRoomUnit().setCanWalk(true);
WiredFreezeUtil.restoreWalkState(this.client.getHabbo().getRoomUnit());
return;
}
@@ -0,0 +1,59 @@
package com.eu.habbo.util;
import com.eu.habbo.Emulator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public final class HotelDateTimeUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(HotelDateTimeUtil.class);
private static final String CONFIG_KEY = "hotel.timezone";
private static volatile String lastInvalidTimezoneId = null;
private HotelDateTimeUtil() {
}
public static String getTimezoneId() {
return getZoneId().getId();
}
public static ZoneId getZoneId() {
String configuredZoneId = Emulator.getConfig().getValue(CONFIG_KEY, ZoneId.systemDefault().getId());
try {
lastInvalidTimezoneId = null;
return ZoneId.of(configuredZoneId.trim());
} catch (Exception e) {
if (!configuredZoneId.equals(lastInvalidTimezoneId)) {
LOGGER.warn("Invalid {} '{}', falling back to system timezone '{}'.", CONFIG_KEY, configuredZoneId, ZoneId.systemDefault().getId());
lastInvalidTimezoneId = configuredZoneId;
}
return ZoneId.systemDefault();
}
}
public static ZonedDateTime now() {
return ZonedDateTime.now(getZoneId());
}
public static LocalDateTime localDateTimeNow() {
return LocalDateTime.now(getZoneId());
}
public static LocalDate localDateNow() {
return LocalDate.now(getZoneId());
}
public static LocalTime localTimeNow() {
return LocalTime.now(getZoneId());
}
public static long toEpochSecond(LocalDateTime dateTime) {
return dateTime.atZone(getZoneId()).toEpochSecond();
}
}