Merge pull request #25 from Lorenzune/pr/wired-advanced-conditions-timezone-20260319

feat(wired): add advanced match conditions and hotel timezone
This commit is contained in:
DuckieTM
2026-03-19 15:07:32 +01:00
committed by GitHub
22 changed files with 2352 additions and 25 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`;
@@ -20,6 +20,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 +54,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 +106,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 +137,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 +221,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 +248,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;
@@ -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());
@@ -297,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;
}
}
}
@@ -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;
}
}
}
@@ -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;
@@ -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);
@@ -279,6 +282,10 @@ public final class WiredManager {
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);
}
@@ -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;
}
}
@@ -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) {
}
@@ -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();
}
}