You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-19 15:06:19 +00:00
Merge pull request #45 from Lorenzune/feature/pr-20260325
Refine wired movement sync, extra behavior, and source handling
This commit is contained in:
Generated
+1
-1
@@ -8,5 +8,5 @@
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_23" default="true" project-jdk-name="openjdk-23" project-jdk-type="JavaSDK" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="graalvm-jdk-21" project-jdk-type="JavaSDK" />
|
||||
</project>
|
||||
@@ -54,6 +54,8 @@ import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraAnimatio
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterFurni;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUser;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMoveCarryUsers;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraExecuteInOrder;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraExecutionLimit;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMovePhysics;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMoveNoAnimation;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraOrEval;
|
||||
@@ -349,6 +351,8 @@ public class ItemManager {
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_mov_no_animation", WiredExtraMoveNoAnimation.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_anim_time", WiredExtraAnimationTime.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_mov_physics", WiredExtraMovePhysics.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_exec_in_order", WiredExtraExecuteInOrder.class));
|
||||
this.interactionsList.add(new ItemInteraction("wf_xtra_execution_limit", WiredExtraExecutionLimit.class));
|
||||
|
||||
|
||||
this.interactionsList.add(new ItemInteraction("wf_highscore", InteractionWiredHighscore.class));
|
||||
|
||||
+1
-8
@@ -162,14 +162,7 @@ public class WiredConditionActorDir extends InteractionWiredCondition {
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
private int normalizeQuantifier(int value) {
|
||||
|
||||
+1
-8
@@ -170,14 +170,7 @@ public class WiredConditionGroupMember extends InteractionWiredCondition {
|
||||
}
|
||||
|
||||
private int normalizeUserSource(int value) {
|
||||
switch (value) {
|
||||
case WiredSourceUtil.SOURCE_TRIGGER:
|
||||
case WiredSourceUtil.SOURCE_SELECTOR:
|
||||
case WiredSourceUtil.SOURCE_SIGNAL:
|
||||
return value;
|
||||
default:
|
||||
return WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
private int normalizeGroupType(int value) {
|
||||
|
||||
+1
-8
@@ -170,14 +170,7 @@ public class WiredConditionNotInGroup extends InteractionWiredCondition {
|
||||
}
|
||||
|
||||
private int normalizeUserSource(int value) {
|
||||
switch (value) {
|
||||
case WiredSourceUtil.SOURCE_TRIGGER:
|
||||
case WiredSourceUtil.SOURCE_SELECTOR:
|
||||
case WiredSourceUtil.SOURCE_SIGNAL:
|
||||
return value;
|
||||
default:
|
||||
return WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
private int normalizeGroupType(int value) {
|
||||
|
||||
+4
@@ -187,6 +187,10 @@ public class WiredConditionSelectionQuantity extends InteractionWiredCondition {
|
||||
}
|
||||
|
||||
private int normalizeSourceType(int group, int value) {
|
||||
if (group == SOURCE_GROUP_USERS) {
|
||||
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
switch (value) {
|
||||
case WiredSourceUtil.SOURCE_SELECTOR:
|
||||
case WiredSourceUtil.SOURCE_SIGNAL:
|
||||
|
||||
+1
-8
@@ -84,14 +84,7 @@ abstract class WiredConditionTeamGameBase extends InteractionWiredCondition {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
protected int normalizePlacement(int value) {
|
||||
|
||||
+3
-12
@@ -304,25 +304,16 @@ public class WiredConditionTriggererMatch extends InteractionWiredCondition {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
private int normalizeCompareUserSource(int value) {
|
||||
switch (value) {
|
||||
case WiredSourceUtil.SOURCE_SELECTOR:
|
||||
case WiredSourceUtil.SOURCE_SIGNAL:
|
||||
case WiredSourceUtil.SOURCE_TRIGGER:
|
||||
case WiredSourceUtil.SOURCE_CLICKED_USER:
|
||||
case SOURCE_SPECIFIED_USERNAME:
|
||||
return value;
|
||||
default:
|
||||
return WiredSourceUtil.SOURCE_TRIGGER;
|
||||
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-8
@@ -185,14 +185,7 @@ public class WiredConditionUserPerformsAction extends InteractionWiredCondition
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
protected int normalizeSignId(int value) {
|
||||
|
||||
+1
-8
@@ -213,14 +213,7 @@ public class WiredEffectBotGiveHandItem extends InteractionWiredEffect {
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
return WiredSourceUtil.isDefaultUserSource(value) ? value : WiredSourceUtil.SOURCE_TRIGGER;
|
||||
}
|
||||
|
||||
private int normalizeBotSource(int value) {
|
||||
|
||||
+92
-9
@@ -4,14 +4,22 @@ 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.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
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.WiredMoveCarryHelper;
|
||||
import com.eu.habbo.messages.outgoing.rooms.WiredMovementsComposer;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class WiredEffectFurniToUser extends WiredEffectUserFurniBase {
|
||||
public static final WiredEffectType type = WiredEffectType.FURNI_TO_USER;
|
||||
@@ -27,27 +35,102 @@ public class WiredEffectFurniToUser extends WiredEffectUserFurniBase {
|
||||
@Override
|
||||
public void execute(WiredContext ctx) {
|
||||
Room room = ctx.room();
|
||||
HabboItem item = this.resolveLastItem(ctx);
|
||||
List<HabboItem> items = new ArrayList<>(this.resolveItems(ctx));
|
||||
Habbo habbo = this.resolveLastHabbo(room, ctx);
|
||||
|
||||
if (room == null || item == null || habbo == null || habbo.getRoomUnit() == null) {
|
||||
if (room == null || habbo == null || habbo.getRoomUnit() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
RoomTile targetTile = habbo.getRoomUnit().getCurrentLocation();
|
||||
items.removeIf(item -> item == null);
|
||||
|
||||
if (items.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
items.sort(Comparator
|
||||
.comparingDouble(HabboItem::getZ)
|
||||
.thenComparingInt(HabboItem::getId));
|
||||
|
||||
Map<Integer, Double> followerZOverrides = new HashMap<>();
|
||||
|
||||
for (HabboItem item : items) {
|
||||
followerZOverrides.put(item.getId(), item.getZ());
|
||||
}
|
||||
|
||||
RoomUnit roomUnit = habbo.getRoomUnit();
|
||||
boolean hasActiveMoveStatus = roomUnit.hasStatus(RoomUnitStatus.MOVE);
|
||||
long moveStatusTimestamp = hasActiveMoveStatus ? roomUnit.getMoveStatusTimestamp() : 0L;
|
||||
|
||||
if (roomUnit.isWalking()) {
|
||||
for (HabboItem item : items) {
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
WiredMoveCarryHelper.registerUserFollower(room, this, item, roomUnit, followerZOverrides.get(item.getId()), ctx);
|
||||
}
|
||||
|
||||
if (!hasActiveMoveStatus) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RoomTile targetTile = this.resolveTargetTile(habbo);
|
||||
if (targetTile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
FurnitureMovementError error = WiredMoveCarryHelper.moveFurni(room, this, item, targetTile, item.getRotation(), null, false, ctx);
|
||||
if (error == FurnitureMovementError.NONE) {
|
||||
return;
|
||||
Integer animationDurationOverride = WiredMoveCarryHelper.hasNoAnimationExtra(room, this)
|
||||
? null
|
||||
: this.resolveFollowAnimationDuration(room, habbo, this);
|
||||
int anchorType = hasActiveMoveStatus ? WiredMovementsComposer.FURNI_ANCHOR_USER : WiredMovementsComposer.FURNI_ANCHOR_NONE;
|
||||
int anchorId = hasActiveMoveStatus ? roomUnit.getId() : 0;
|
||||
|
||||
if (hasActiveMoveStatus) {
|
||||
int animationDuration = WiredMoveCarryHelper.resolveMoveStepDuration(roomUnit);
|
||||
int animationElapsed = WiredMoveCarryHelper.resolveMoveStepElapsed(roomUnit);
|
||||
|
||||
for (HabboItem item : items) {
|
||||
if (item == null || WiredMoveCarryHelper.isUserFollowerProcessed(roomUnit, item, moveStatusTimestamp)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Double targetZ = WiredMoveCarryHelper.resolveFollowerStackZ(room, item, targetTile, item.getRotation());
|
||||
FurnitureMovementError error = WiredMoveCarryHelper.moveFurni(room, this, item, targetTile, item.getRotation(), targetZ, null, false, ctx, animationDuration, animationElapsed, WiredMovementsComposer.FURNI_ANCHOR_USER, roomUnit.getId());
|
||||
if (error != FurnitureMovementError.NONE) {
|
||||
Double fallbackZ = followerZOverrides.get(item.getId());
|
||||
|
||||
if (fallbackZ != null) {
|
||||
error = WiredMoveCarryHelper.moveFurni(room, this, item, targetTile, item.getRotation(), fallbackZ, null, false, ctx, animationDuration, animationElapsed, WiredMovementsComposer.FURNI_ANCHOR_USER, roomUnit.getId());
|
||||
}
|
||||
}
|
||||
|
||||
if (error == FurnitureMovementError.NONE) {
|
||||
WiredMoveCarryHelper.markUserFollowerProcessed(roomUnit, item, moveStatusTimestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.getBaseItem().getStateCount() > 0) {
|
||||
error = WiredMoveCarryHelper.moveFurni(room, this, item, targetTile, item.getRotation(), item.getZ(), null, false, ctx);
|
||||
for (HabboItem item : items) {
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hasActiveMoveStatus && WiredMoveCarryHelper.isUserFollowerProcessed(roomUnit, item, moveStatusTimestamp)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Double targetZ = WiredMoveCarryHelper.resolveFollowerStackZ(room, item, targetTile, item.getRotation());
|
||||
FurnitureMovementError error = WiredMoveCarryHelper.moveFurni(room, this, item, targetTile, item.getRotation(), targetZ, null, false, ctx, animationDurationOverride, null, anchorType, anchorId);
|
||||
if (error == FurnitureMovementError.NONE) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
Double fallbackZ = followerZOverrides.get(item.getId());
|
||||
|
||||
if (fallbackZ != null) {
|
||||
WiredMoveCarryHelper.moveFurni(room, this, item, targetTile, item.getRotation(), fallbackZ, null, false, ctx, animationDurationOverride, null, anchorType, anchorId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+67
-1
@@ -7,15 +7,19 @@ 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.RoomTile;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnit;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
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.WiredMoveCarryHelper;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.incoming.wired.WiredSaveException;
|
||||
import com.eu.habbo.messages.outgoing.rooms.WiredMovementsComposer;
|
||||
import gnu.trove.procedure.TObjectProcedure;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
@@ -37,7 +41,7 @@ public abstract class WiredEffectUserFurniBase extends InteractionWiredEffect {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
protected HabboItem resolveLastItem(WiredContext ctx) {
|
||||
protected List<HabboItem> resolveItems(WiredContext ctx) {
|
||||
Room room = ctx.room();
|
||||
List<HabboItem> effectiveItems = WiredSourceUtil.resolveItems(ctx, this.furniSource, this.items);
|
||||
|
||||
@@ -47,6 +51,12 @@ public abstract class WiredEffectUserFurniBase extends InteractionWiredEffect {
|
||||
|| room.getHabboItem(item.getId()) == null);
|
||||
}
|
||||
|
||||
return effectiveItems;
|
||||
}
|
||||
|
||||
protected HabboItem resolveLastItem(WiredContext ctx) {
|
||||
List<HabboItem> effectiveItems = this.resolveItems(ctx);
|
||||
|
||||
if (effectiveItems.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
@@ -90,6 +100,62 @@ public abstract class WiredEffectUserFurniBase extends InteractionWiredEffect {
|
||||
return habbos;
|
||||
}
|
||||
|
||||
protected RoomTile resolveTargetTile(Habbo habbo) {
|
||||
if (habbo == null || habbo.getRoomUnit() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
RoomUnit roomUnit = habbo.getRoomUnit();
|
||||
RoomTile movingTile = this.resolveActiveMoveTile(roomUnit);
|
||||
|
||||
if (movingTile != null) {
|
||||
return movingTile;
|
||||
}
|
||||
|
||||
return roomUnit.getCurrentLocation();
|
||||
}
|
||||
|
||||
private RoomTile resolveActiveMoveTile(RoomUnit roomUnit) {
|
||||
if (roomUnit == null || roomUnit.getRoom() == null || roomUnit.getRoom().getLayout() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String moveStatus = roomUnit.getStatus(RoomUnitStatus.MOVE);
|
||||
if (moveStatus != null && !moveStatus.isEmpty()) {
|
||||
String[] parts = moveStatus.split(",");
|
||||
if (parts.length >= 2) {
|
||||
try {
|
||||
return roomUnit.getRoom().getLayout().getTile(
|
||||
Short.parseShort(parts[0]),
|
||||
Short.parseShort(parts[1]));
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Integer resolveFollowAnimationDuration(Room room, Habbo habbo, HabboItem stackItem) {
|
||||
if (room == null || habbo == null || habbo.getRoomUnit() == null || stackItem == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
RoomUnit roomUnit = habbo.getRoomUnit();
|
||||
if (this.resolveActiveMoveTile(roomUnit) == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
long moveStatusTimestamp = roomUnit.getMoveStatusTimestamp();
|
||||
if (moveStatusTimestamp <= 0L) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int configuredDuration = WiredMoveCarryHelper.getAnimationDuration(room, stackItem, WiredMovementsComposer.DEFAULT_DURATION);
|
||||
int remainingStepDuration = (int) Math.max(50L, WiredMovementsComposer.DEFAULT_DURATION - Math.max(0L, System.currentTimeMillis() - moveStatusTimestamp));
|
||||
return Math.min(configuredDuration, remainingStepDuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return WiredManager.getGson().toJson(new JsonData(
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
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.core.WiredManager;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class WiredExtraExecuteInOrder extends InteractionWiredExtra {
|
||||
public static final int CODE = 64;
|
||||
|
||||
public WiredExtraExecuteInOrder(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredExtraExecuteInOrder(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return WiredManager.getGson().toJson(new JsonData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString("");
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(CODE);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
}
|
||||
}
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
|
||||
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
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.wired.core.WiredManager;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
public class WiredExtraExecutionLimit extends InteractionWiredExtra {
|
||||
public static final int CODE = 65;
|
||||
public static final int MIN_EXECUTIONS = 1;
|
||||
public static final int MAX_EXECUTIONS = 100;
|
||||
public static final int DEFAULT_EXECUTIONS = 1;
|
||||
public static final int MIN_TIME_WINDOW_MS = 1000;
|
||||
public static final int MAX_TIME_WINDOW_MS = 10000;
|
||||
public static final int DEFAULT_TIME_WINDOW_MS = 1000;
|
||||
public static final int TIME_WINDOW_STEP_MS = 500;
|
||||
|
||||
private final Deque<Long> recentExecutionTimestamps = new ArrayDeque<>();
|
||||
private int maxExecutions = DEFAULT_EXECUTIONS;
|
||||
private int timeWindowMs = DEFAULT_TIME_WINDOW_MS;
|
||||
|
||||
public WiredExtraExecutionLimit(ResultSet set, Item baseItem) throws SQLException {
|
||||
super(set, baseItem);
|
||||
}
|
||||
|
||||
public WiredExtraExecutionLimit(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
|
||||
super(id, userId, item, extradata, limitedStack, limitedSells);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData(WiredSettings settings, GameClient gameClient) {
|
||||
int[] intParams = settings.getIntParams();
|
||||
int nextExecutions = (intParams.length > 0) ? intParams[0] : this.maxExecutions;
|
||||
int nextTimeWindowMs = (intParams.length > 1) ? intParams[1] : this.timeWindowMs;
|
||||
|
||||
this.maxExecutions = normalizeExecutions(nextExecutions);
|
||||
this.timeWindowMs = normalizeTimeWindowMs(nextTimeWindowMs);
|
||||
clearRuntimeState();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWiredData() {
|
||||
return WiredManager.getGson().toJson(new JsonData(this.maxExecutions, this.timeWindowMs));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWiredData(ServerMessage message, Room room) {
|
||||
message.appendBoolean(false);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
message.appendInt(this.getBaseItem().getSpriteId());
|
||||
message.appendInt(this.getId());
|
||||
message.appendString("");
|
||||
message.appendInt(2);
|
||||
message.appendInt(this.maxExecutions);
|
||||
message.appendInt(this.timeWindowMs);
|
||||
message.appendInt(0);
|
||||
message.appendInt(CODE);
|
||||
message.appendInt(0);
|
||||
message.appendInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWiredData(ResultSet set, Room room) throws SQLException {
|
||||
this.onPickUp();
|
||||
|
||||
String wiredData = set.getString("wired_data");
|
||||
if (wiredData == null || wiredData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wiredData.startsWith("{")) {
|
||||
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
|
||||
|
||||
if (data != null) {
|
||||
this.maxExecutions = normalizeExecutions(data.maxExecutions);
|
||||
this.timeWindowMs = normalizeTimeWindowMs(data.timeWindowMs);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
String[] legacyData = wiredData.split(";");
|
||||
|
||||
try {
|
||||
if (legacyData.length > 0) {
|
||||
this.maxExecutions = normalizeExecutions(Integer.parseInt(legacyData[0]));
|
||||
}
|
||||
|
||||
if (legacyData.length > 1) {
|
||||
this.timeWindowMs = normalizeTimeWindowMs(Integer.parseInt(legacyData[1]));
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
this.maxExecutions = DEFAULT_EXECUTIONS;
|
||||
this.timeWindowMs = DEFAULT_TIME_WINDOW_MS;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickUp() {
|
||||
this.maxExecutions = DEFAULT_EXECUTIONS;
|
||||
this.timeWindowMs = DEFAULT_TIME_WINDOW_MS;
|
||||
clearRuntimeState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMove(Room room, RoomTile oldLocation, RoomTile newLocation) {
|
||||
super.onMove(room, oldLocation, newLocation);
|
||||
clearRuntimeState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean tryAcquireExecutionSlot(long timestamp) {
|
||||
synchronized (this.recentExecutionTimestamps) {
|
||||
pruneExpiredTimestamps(timestamp);
|
||||
|
||||
if (this.recentExecutionTimestamps.size() >= this.maxExecutions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.recentExecutionTimestamps.addLast(timestamp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canExecuteAt(long timestamp) {
|
||||
synchronized (this.recentExecutionTimestamps) {
|
||||
pruneExpiredTimestamps(timestamp);
|
||||
return this.recentExecutionTimestamps.size() < this.maxExecutions;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaxExecutions() {
|
||||
return this.maxExecutions;
|
||||
}
|
||||
|
||||
public int getTimeWindowMs() {
|
||||
return this.timeWindowMs;
|
||||
}
|
||||
|
||||
public void clearRuntimeState() {
|
||||
synchronized (this.recentExecutionTimestamps) {
|
||||
this.recentExecutionTimestamps.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void pruneExpiredTimestamps(long timestamp) {
|
||||
while (!this.recentExecutionTimestamps.isEmpty()
|
||||
&& (timestamp - this.recentExecutionTimestamps.peekFirst()) >= this.timeWindowMs) {
|
||||
this.recentExecutionTimestamps.removeFirst();
|
||||
}
|
||||
}
|
||||
|
||||
private static int normalizeExecutions(int value) {
|
||||
return Math.max(MIN_EXECUTIONS, Math.min(MAX_EXECUTIONS, value));
|
||||
}
|
||||
|
||||
private static int normalizeTimeWindowMs(int value) {
|
||||
if (value < MIN_TIME_WINDOW_MS) {
|
||||
return MIN_TIME_WINDOW_MS;
|
||||
}
|
||||
|
||||
if (value > MAX_TIME_WINDOW_MS) {
|
||||
return MAX_TIME_WINDOW_MS;
|
||||
}
|
||||
|
||||
return Math.round(value / (float) TIME_WINDOW_STEP_MS) * TIME_WINDOW_STEP_MS;
|
||||
}
|
||||
|
||||
static class JsonData {
|
||||
int maxExecutions;
|
||||
int timeWindowMs;
|
||||
|
||||
JsonData(int maxExecutions, int timeWindowMs) {
|
||||
this.maxExecutions = maxExecutions;
|
||||
this.timeWindowMs = timeWindowMs;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-4
@@ -155,12 +155,20 @@ public class WiredTriggerAtSetTime extends InteractionWiredTrigger implements Wi
|
||||
|
||||
// Check if enough time has passed
|
||||
if (this.accumulatedTime >= this.executeTime) {
|
||||
if (this.getRoomId() != 0 && room.isLoaded()) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (!WiredManager.isTriggerExecutionAllowed(room, this, currentTime)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hasFired = true;
|
||||
this.accumulatedTime = 0;
|
||||
WiredManager.triggerTimerTick(room, this);
|
||||
return;
|
||||
}
|
||||
|
||||
this.hasFired = true;
|
||||
this.accumulatedTime = 0;
|
||||
|
||||
if (this.getRoomId() != 0 && room.isLoaded()) {
|
||||
WiredManager.triggerTimerTick(room, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-4
@@ -155,12 +155,20 @@ public class WiredTriggerAtTimeLong extends InteractionWiredTrigger implements W
|
||||
|
||||
// Check if enough time has passed
|
||||
if (this.accumulatedTime >= this.executeTime) {
|
||||
if (this.getRoomId() != 0 && room.isLoaded()) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (!WiredManager.isTriggerExecutionAllowed(room, this, currentTime)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hasFired = true;
|
||||
this.accumulatedTime = 0;
|
||||
WiredManager.triggerTimerTick(room, this);
|
||||
return;
|
||||
}
|
||||
|
||||
this.hasFired = true;
|
||||
this.accumulatedTime = 0;
|
||||
|
||||
if (this.getRoomId() != 0 && room.isLoaded()) {
|
||||
WiredManager.triggerTimerTick(room, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -144,7 +144,9 @@ public class WiredTriggerRepeater extends InteractionWiredTrigger implements Wir
|
||||
|
||||
// Fire when elapsed time is a multiple of repeatTime
|
||||
if (elapsedMs % this.repeatTime == 0) {
|
||||
if (this.getRoomId() != 0 && room.isLoaded()) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (this.getRoomId() != 0 && room.isLoaded()
|
||||
&& WiredManager.isTriggerExecutionAllowed(room, this, currentTime)) {
|
||||
WiredManager.triggerTimerRepeat(room, this);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -138,7 +138,9 @@ 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()) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (this.getRoomId() != 0 && room.isLoaded()
|
||||
&& WiredManager.isTriggerExecutionAllowed(room, this, currentTime)) {
|
||||
WiredManager.triggerTimerRepeatLong(room, this);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -105,7 +105,9 @@ public class WiredTriggerRepeaterShort extends WiredTriggerRepeater {
|
||||
long elapsedMs = tickCount * tickIntervalMs;
|
||||
|
||||
if (elapsedMs % this.repeatTime == 0) {
|
||||
if (this.getRoomId() != 0 && room.isLoaded()) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (this.getRoomId() != 0 && room.isLoaded()
|
||||
&& WiredManager.isTriggerExecutionAllowed(room, this, currentTime)) {
|
||||
WiredManager.triggerTimerRepeatShort(room, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2032,6 +2032,10 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
||||
this.messagingManager.sendComposer(message);
|
||||
}
|
||||
|
||||
public void sendComposers(Collection<ServerMessage> messages) {
|
||||
this.messagingManager.sendComposers(messages);
|
||||
}
|
||||
|
||||
public void sendComposerToHabbosWithRights(ServerMessage message) {
|
||||
this.messagingManager.sendComposerToHabbosWithRights(message);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ 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.core.WiredManager;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredMoveCarryHelper;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.outgoing.rooms.RoomAccessDeniedComposer;
|
||||
import com.eu.habbo.messages.outgoing.rooms.users.RoomUnitIdleComposer;
|
||||
@@ -123,7 +124,19 @@ public class RoomCycleManager {
|
||||
|
||||
// Send status updates
|
||||
if (!updatedUnit.isEmpty()) {
|
||||
this.room.sendComposer(new RoomUserStatusComposer(updatedUnit, true).compose());
|
||||
ServerMessage statusComposer = new RoomUserStatusComposer(updatedUnit, true).compose();
|
||||
WiredMoveCarryHelper.beginMovementCollection();
|
||||
WiredMoveCarryHelper.processUserFollowers(this.room, updatedUnit);
|
||||
ServerMessage wiredMovementsComposer = WiredMoveCarryHelper.finishMovementCollection();
|
||||
|
||||
if (wiredMovementsComposer != null) {
|
||||
ArrayList<ServerMessage> batchedMessages = new ArrayList<>(2);
|
||||
batchedMessages.add(statusComposer);
|
||||
batchedMessages.add(wiredMovementsComposer);
|
||||
this.room.sendComposers(batchedMessages);
|
||||
} else {
|
||||
this.room.sendComposer(statusComposer);
|
||||
}
|
||||
}
|
||||
|
||||
// Cycle trax manager
|
||||
|
||||
@@ -4,6 +4,9 @@ import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.outgoing.generic.alerts.GenericAlertComposer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Manages all messaging and communication within a room.
|
||||
* Handles sending messages to Habbos, pet/bot chat, and alerts.
|
||||
@@ -30,6 +33,34 @@ public class RoomMessagingManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void sendComposers(Collection<ServerMessage> messages) {
|
||||
if (messages == null || messages.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<ServerMessage> responses = new ArrayList<>();
|
||||
|
||||
for (ServerMessage message : messages) {
|
||||
if (message == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
responses.add(message);
|
||||
}
|
||||
|
||||
if (responses.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Habbo habbo : this.room.getHabbos()) {
|
||||
if (habbo.getClient() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
habbo.getClient().sendResponses(new ArrayList<>(responses));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to all Habbos with rights in the room.
|
||||
*/
|
||||
|
||||
@@ -74,6 +74,7 @@ public class RoomUnit {
|
||||
private int handItem;
|
||||
private long handItemTimestamp;
|
||||
private long lastRollerTime;
|
||||
private long moveStatusTimestamp;
|
||||
private int walkTimeOut;
|
||||
private int effectId;
|
||||
private int effectEndTimestamp;
|
||||
@@ -104,6 +105,7 @@ public class RoomUnit {
|
||||
this.goalLocation = null;
|
||||
this.startLocation = this.currentLocation;
|
||||
this.inRoom = false;
|
||||
this.moveStatusTimestamp = 0L;
|
||||
|
||||
this.status.clear();
|
||||
|
||||
@@ -611,12 +613,16 @@ public class RoomUnit {
|
||||
}
|
||||
|
||||
public void removeStatus(RoomUnitStatus key) {
|
||||
if (key == RoomUnitStatus.MOVE) {
|
||||
this.moveStatusTimestamp = 0L;
|
||||
}
|
||||
this.status.remove(key);
|
||||
}
|
||||
|
||||
public void setStatus(RoomUnitStatus key, String value) {
|
||||
if (key != null && value != null) {
|
||||
if (key == RoomUnitStatus.MOVE) {
|
||||
this.moveStatusTimestamp = System.currentTimeMillis();
|
||||
WiredMoveCarryHelper.clearStatusComposerSuppression(this);
|
||||
WiredUserMovementHelper.clearStatusComposerSuppression(this);
|
||||
}
|
||||
@@ -630,6 +636,7 @@ public class RoomUnit {
|
||||
}
|
||||
|
||||
public void clearStatus() {
|
||||
this.moveStatusTimestamp = 0L;
|
||||
this.status.clear();
|
||||
}
|
||||
|
||||
@@ -657,6 +664,10 @@ public class RoomUnit {
|
||||
this.lastRollerTime = lastRollerTime;
|
||||
}
|
||||
|
||||
public long getMoveStatusTimestamp() {
|
||||
return this.moveStatusTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if enough time has passed since the last roller movement to allow rolling again.
|
||||
* This prevents desync issues where the client hasn't finished the roller animation.
|
||||
|
||||
@@ -10,6 +10,8 @@ import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTriggerReset;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.effects.WiredEffectGiveReward;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.effects.WiredEffectTriggerStacks;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraExecuteInOrder;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraExecutionLimit;
|
||||
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.rooms.Room;
|
||||
@@ -27,6 +29,7 @@ import com.eu.habbo.plugin.events.furniture.wired.WiredConditionFailedEvent;
|
||||
import com.eu.habbo.plugin.events.furniture.wired.WiredStackExecutedEvent;
|
||||
import com.eu.habbo.plugin.events.furniture.wired.WiredStackTriggeredEvent;
|
||||
import com.eu.habbo.plugin.events.users.UserWiredRewardReceived;
|
||||
import com.eu.habbo.habbohotel.wired.core.WiredExecutionOrderUtil;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
import org.slf4j.Logger;
|
||||
@@ -37,7 +40,7 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
||||
public class WiredHandler {
|
||||
@@ -49,6 +52,11 @@ public class WiredHandler {
|
||||
|
||||
private static GsonBuilder gsonBuilder = null;
|
||||
|
||||
private static final class LegacyExecutionPlan {
|
||||
private final LinkedHashSet<InteractionWiredEffect> effects = new LinkedHashSet<>();
|
||||
private boolean executeInOrder = false;
|
||||
}
|
||||
|
||||
public static boolean handle(WiredTriggerType triggerType, RoomUnit roomUnit, Room room, Object[] stuff) {
|
||||
if (triggerType == WiredTriggerType.CUSTOM) return false;
|
||||
|
||||
@@ -72,7 +80,7 @@ public class WiredHandler {
|
||||
return false;
|
||||
|
||||
long millis = System.currentTimeMillis();
|
||||
THashSet<InteractionWiredEffect> effectsToExecute = new THashSet<InteractionWiredEffect>();
|
||||
List<LegacyExecutionPlan> executionPlans = new ArrayList<>();
|
||||
|
||||
List<RoomTile> triggeredTiles = new ArrayList<>();
|
||||
for (InteractionWiredTrigger trigger : triggers) {
|
||||
@@ -81,10 +89,10 @@ public class WiredHandler {
|
||||
if (triggeredTiles.contains(tile))
|
||||
continue;
|
||||
|
||||
THashSet<InteractionWiredEffect> tEffectsToExecute = new THashSet<InteractionWiredEffect>();
|
||||
LegacyExecutionPlan executionPlan = new LegacyExecutionPlan();
|
||||
|
||||
if (handle(trigger, roomUnit, room, stuff, tEffectsToExecute)) {
|
||||
effectsToExecute.addAll(tEffectsToExecute);
|
||||
if (handle(trigger, roomUnit, room, stuff, executionPlan)) {
|
||||
executionPlans.add(executionPlan);
|
||||
|
||||
if (triggerType.equals(WiredTriggerType.SAY_SOMETHING))
|
||||
talked = true;
|
||||
@@ -93,8 +101,8 @@ public class WiredHandler {
|
||||
}
|
||||
}
|
||||
|
||||
for (InteractionWiredEffect effect : effectsToExecute) {
|
||||
triggerEffect(effect, roomUnit, room, stuff, millis);
|
||||
for (LegacyExecutionPlan executionPlan : executionPlans) {
|
||||
triggerEffects(executionPlan.effects, roomUnit, room, stuff, millis, executionPlan.executeInOrder);
|
||||
}
|
||||
|
||||
return talked;
|
||||
@@ -119,7 +127,7 @@ public class WiredHandler {
|
||||
return false;
|
||||
|
||||
long millis = System.currentTimeMillis();
|
||||
THashSet<InteractionWiredEffect> effectsToExecute = new THashSet<InteractionWiredEffect>();
|
||||
List<LegacyExecutionPlan> executionPlans = new ArrayList<>();
|
||||
|
||||
List<RoomTile> triggeredTiles = new ArrayList<>();
|
||||
for (InteractionWiredTrigger trigger : triggers) {
|
||||
@@ -130,44 +138,51 @@ public class WiredHandler {
|
||||
if (triggeredTiles.contains(tile))
|
||||
continue;
|
||||
|
||||
THashSet<InteractionWiredEffect> tEffectsToExecute = new THashSet<InteractionWiredEffect>();
|
||||
LegacyExecutionPlan executionPlan = new LegacyExecutionPlan();
|
||||
|
||||
if (handle(trigger, roomUnit, room, stuff, tEffectsToExecute)) {
|
||||
effectsToExecute.addAll(tEffectsToExecute);
|
||||
if (handle(trigger, roomUnit, room, stuff, executionPlan)) {
|
||||
executionPlans.add(executionPlan);
|
||||
triggeredTiles.add(tile);
|
||||
}
|
||||
}
|
||||
|
||||
for (InteractionWiredEffect effect : effectsToExecute) {
|
||||
triggerEffect(effect, roomUnit, room, stuff, millis);
|
||||
for (LegacyExecutionPlan executionPlan : executionPlans) {
|
||||
triggerEffects(executionPlan.effects, roomUnit, room, stuff, millis, executionPlan.executeInOrder);
|
||||
}
|
||||
|
||||
return effectsToExecute.size() > 0;
|
||||
return !executionPlans.isEmpty();
|
||||
}
|
||||
|
||||
public static boolean handle(InteractionWiredTrigger trigger, final RoomUnit roomUnit, final Room room, final Object[] stuff) {
|
||||
long millis = System.currentTimeMillis();
|
||||
THashSet<InteractionWiredEffect> effectsToExecute = new THashSet<InteractionWiredEffect>();
|
||||
LegacyExecutionPlan executionPlan = new LegacyExecutionPlan();
|
||||
|
||||
if(handle(trigger, roomUnit, room, stuff, effectsToExecute)) {
|
||||
for (InteractionWiredEffect effect : effectsToExecute) {
|
||||
triggerEffect(effect, roomUnit, room, stuff, millis);
|
||||
}
|
||||
if(handle(trigger, roomUnit, room, stuff, executionPlan)) {
|
||||
triggerEffects(executionPlan.effects, roomUnit, room, stuff, millis, executionPlan.executeInOrder);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean handle(InteractionWiredTrigger trigger, final RoomUnit roomUnit, final Room room, final Object[] stuff, final THashSet<InteractionWiredEffect> effectsToExecute) {
|
||||
private static boolean handle(InteractionWiredTrigger trigger, final RoomUnit roomUnit, final Room room, final Object[] stuff, final LegacyExecutionPlan executionPlan) {
|
||||
long millis = System.currentTimeMillis();
|
||||
int roomUnitId = roomUnit != null ? roomUnit.getId() : -1;
|
||||
if (Emulator.isReady && ((Emulator.getConfig().getBoolean("wired.custom.enabled", false) && (trigger.canExecute(millis) || roomUnitId > -1) && trigger.userCanExecute(roomUnitId, millis)) || (!Emulator.getConfig().getBoolean("wired.custom.enabled", false) && trigger.canExecute(millis))) && trigger.execute(roomUnit, room, stuff)) {
|
||||
trigger.activateBox(room, roomUnit, millis);
|
||||
|
||||
THashSet<InteractionWiredCondition> conditions = room.getRoomSpecialTypes().getConditions(trigger.getX(), trigger.getY());
|
||||
THashSet<InteractionWiredEffect> effects = room.getRoomSpecialTypes().getEffects(trigger.getX(), trigger.getY());
|
||||
if (Emulator.getPluginManager().fireEvent(new WiredStackTriggeredEvent(room, roomUnit, trigger, effects, conditions)).isCancelled())
|
||||
return false;
|
||||
THashSet<InteractionWiredExtra> extras = room.getRoomSpecialTypes().getExtras(trigger.getX(), trigger.getY());
|
||||
WiredExtraExecutionLimit executionLimitExtra = null;
|
||||
WiredExtraRandom randomExtra = null;
|
||||
|
||||
for (InteractionWiredExtra extra : extras) {
|
||||
if (executionLimitExtra == null && extra instanceof WiredExtraExecutionLimit) {
|
||||
executionLimitExtra = (WiredExtraExecutionLimit) extra;
|
||||
}
|
||||
|
||||
if (randomExtra == null && extra instanceof WiredExtraRandom) {
|
||||
randomExtra = (WiredExtraRandom) extra;
|
||||
}
|
||||
}
|
||||
|
||||
if (!conditions.isEmpty()) {
|
||||
ArrayList<WiredConditionType> matchedConditions = new ArrayList<>(conditions.size());
|
||||
@@ -187,39 +202,48 @@ public class WiredHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if (executionLimitExtra != null && !executionLimitExtra.tryAcquireExecutionSlot(millis)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Emulator.getPluginManager().fireEvent(new WiredStackTriggeredEvent(room, roomUnit, trigger, effects, conditions)).isCancelled())
|
||||
return false;
|
||||
|
||||
trigger.activateBox(room, roomUnit, millis);
|
||||
|
||||
trigger.setCooldown(millis);
|
||||
|
||||
boolean hasExtraUnseen = room.getRoomSpecialTypes().hasExtraType(trigger.getX(), trigger.getY(), WiredExtraUnseen.class);
|
||||
THashSet<InteractionWiredExtra> extras = room.getRoomSpecialTypes().getExtras(trigger.getX(), trigger.getY());
|
||||
WiredExtraRandom randomExtra = null;
|
||||
boolean hasExtraExecuteInOrder = room.getRoomSpecialTypes().hasExtraType(trigger.getX(), trigger.getY(), WiredExtraExecuteInOrder.class);
|
||||
|
||||
for (InteractionWiredExtra extra : extras) {
|
||||
extra.activateBox(room, roomUnit, millis);
|
||||
if (randomExtra == null && extra instanceof WiredExtraRandom) {
|
||||
randomExtra = (WiredExtraRandom) extra;
|
||||
}
|
||||
}
|
||||
|
||||
List<InteractionWiredEffect> effectList = new ArrayList<>(effects);
|
||||
List<InteractionWiredEffect> effectList = (hasExtraUnseen || hasExtraExecuteInOrder)
|
||||
? WiredExecutionOrderUtil.sort(effects)
|
||||
: new ArrayList<>(effects);
|
||||
|
||||
if (randomExtra != null || hasExtraUnseen) {
|
||||
Collections.shuffle(effectList);
|
||||
}
|
||||
executionPlan.executeInOrder = hasExtraExecuteInOrder;
|
||||
|
||||
if (hasExtraUnseen) {
|
||||
for (InteractionWiredExtra extra : room.getRoomSpecialTypes().getExtras(trigger.getX(), trigger.getY())) {
|
||||
if (extra instanceof WiredExtraUnseen) {
|
||||
extra.setExtradata(extra.getExtradata().equals("1") ? "0" : "1");
|
||||
InteractionWiredEffect effect = ((WiredExtraUnseen) extra).getUnseenEffect(effectList);
|
||||
effectsToExecute.add(effect); // triggerEffect(effect, roomUnit, room, stuff, millis);
|
||||
if (effect != null) {
|
||||
executionPlan.effects.add(effect);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (randomExtra != null) {
|
||||
effectsToExecute.addAll(randomExtra.selectEffects(effectList));
|
||||
executionPlan.effects.addAll(randomExtra.selectEffects(effectList));
|
||||
} else if (hasExtraExecuteInOrder) {
|
||||
executionPlan.effects.addAll(effectList);
|
||||
} else {
|
||||
for (final InteractionWiredEffect effect : effectList) {
|
||||
effectsToExecute.add(effect); //triggerEffect(effect, roomUnit, room, stuff, millis);
|
||||
executionPlan.effects.add(effect);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +258,7 @@ public class WiredHandler {
|
||||
if (effect != null && (effect.canExecute(millis) || (roomUnit != null && effect.requiresTriggeringUser() && Emulator.getConfig().getBoolean("wired.custom.enabled", false) && effect.userCanExecute(roomUnit.getId(), millis)))) {
|
||||
executed = true;
|
||||
if (!effect.requiresTriggeringUser() || (roomUnit != null && effect.requiresTriggeringUser())) {
|
||||
Emulator.getThreading().run(() -> {
|
||||
Runnable execution = () -> {
|
||||
if (room.isLoaded() && room.getHabbos().size() > 0) {
|
||||
try {
|
||||
if (!effect.execute(roomUnit, room, stuff)) return;
|
||||
@@ -245,13 +269,108 @@ public class WiredHandler {
|
||||
|
||||
effect.activateBox(room, roomUnit, millis);
|
||||
}
|
||||
}, effect.getDelay() * 500L);
|
||||
};
|
||||
|
||||
long delayMs = effect.getDelay() * 500L;
|
||||
long elapsedSinceTrigger = Math.max(0L, System.currentTimeMillis() - millis);
|
||||
long remainingDelayMs = Math.max(0L, delayMs - elapsedSinceTrigger);
|
||||
|
||||
if (delayMs <= 0) {
|
||||
execution.run();
|
||||
} else {
|
||||
Emulator.getThreading().run(execution, remainingDelayMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return executed;
|
||||
}
|
||||
|
||||
private static void triggerEffects(LinkedHashSet<InteractionWiredEffect> effects, RoomUnit roomUnit, Room room, Object[] stuff, long millis, boolean executeInOrder) {
|
||||
if (effects == null || effects.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!executeInOrder) {
|
||||
for (InteractionWiredEffect effect : effects) {
|
||||
triggerEffect(effect, roomUnit, room, stuff, millis);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
LinkedHashSet<InteractionWiredEffect> queueableEffects = new LinkedHashSet<>();
|
||||
|
||||
for (InteractionWiredEffect effect : effects) {
|
||||
if (canQueueEffect(effect, roomUnit, millis)) {
|
||||
queueableEffects.add(effect);
|
||||
}
|
||||
}
|
||||
|
||||
LinkedHashSet<Integer> delays = new LinkedHashSet<>();
|
||||
for (InteractionWiredEffect effect : queueableEffects) {
|
||||
delays.add(effect.getDelay());
|
||||
}
|
||||
|
||||
for (Integer delay : delays) {
|
||||
List<InteractionWiredEffect> delayBatch = new ArrayList<>();
|
||||
|
||||
for (InteractionWiredEffect effect : queueableEffects) {
|
||||
if (effect.getDelay() == delay) {
|
||||
delayBatch.add(effect);
|
||||
}
|
||||
}
|
||||
|
||||
if (delayBatch.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (delay > 0) {
|
||||
long delayMs = delay * 500L;
|
||||
long elapsedSinceTrigger = Math.max(0L, System.currentTimeMillis() - millis);
|
||||
long remainingDelayMs = Math.max(0L, delayMs - elapsedSinceTrigger);
|
||||
Emulator.getThreading().run(() -> executeOrderedEffectBatch(delayBatch, roomUnit, room, stuff, millis), remainingDelayMs);
|
||||
} else {
|
||||
executeOrderedEffectBatch(delayBatch, roomUnit, room, stuff, millis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canQueueEffect(InteractionWiredEffect effect, RoomUnit roomUnit, long millis) {
|
||||
if (effect == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean canExecute = effect.canExecute(millis)
|
||||
|| (roomUnit != null && effect.requiresTriggeringUser()
|
||||
&& Emulator.getConfig().getBoolean("wired.custom.enabled", false)
|
||||
&& effect.userCanExecute(roomUnit.getId(), millis));
|
||||
|
||||
if (!canExecute) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !effect.requiresTriggeringUser() || roomUnit != null;
|
||||
}
|
||||
|
||||
private static void executeOrderedEffectBatch(List<InteractionWiredEffect> effects, RoomUnit roomUnit, Room room, Object[] stuff, long millis) {
|
||||
if (!room.isLoaded() || room.getHabbos().size() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (InteractionWiredEffect effect : effects) {
|
||||
try {
|
||||
if (!effect.execute(roomUnit, room, stuff)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
effect.setCooldown(millis);
|
||||
effect.activateBox(room, roomUnit, millis);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Caught exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static GsonBuilder getGsonBuilder() {
|
||||
if(gsonBuilder == null) {
|
||||
gsonBuilder = new GsonBuilder();
|
||||
|
||||
@@ -39,6 +39,7 @@ public final class WiredStack {
|
||||
private final boolean useOrMode; // WiredExtraOrEval present
|
||||
private final boolean useRandom; // WiredExtraRandom present
|
||||
private final boolean useUnseen; // WiredExtraUnseen present
|
||||
private final boolean executeInOrder; // WiredExtraExecuteInOrder present
|
||||
|
||||
/**
|
||||
* Create a new wired stack.
|
||||
@@ -52,7 +53,7 @@ public final class WiredStack {
|
||||
IWiredTrigger trigger,
|
||||
List<IWiredCondition> conditions,
|
||||
List<IWiredEffect> effects) {
|
||||
this(triggerItem, trigger, conditions, effects, false, false, false);
|
||||
this(triggerItem, trigger, conditions, effects, false, false, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,6 +66,7 @@ public final class WiredStack {
|
||||
* @param useOrMode if true, conditions use OR logic (any pass = success)
|
||||
* @param useRandom if true, select one random effect instead of all
|
||||
* @param useUnseen if true, execute effects in "unseen" order (round-robin)
|
||||
* @param executeInOrder if true, execute all regular effects in stable stack order
|
||||
*/
|
||||
public WiredStack(HabboItem triggerItem,
|
||||
IWiredTrigger trigger,
|
||||
@@ -72,7 +74,8 @@ public final class WiredStack {
|
||||
List<IWiredEffect> effects,
|
||||
boolean useOrMode,
|
||||
boolean useRandom,
|
||||
boolean useUnseen) {
|
||||
boolean useUnseen,
|
||||
boolean executeInOrder) {
|
||||
this.triggerItem = triggerItem;
|
||||
this.trigger = trigger;
|
||||
this.conditions = conditions != null ? Collections.unmodifiableList(conditions) : Collections.emptyList();
|
||||
@@ -80,6 +83,7 @@ public final class WiredStack {
|
||||
this.useOrMode = useOrMode;
|
||||
this.useRandom = useRandom;
|
||||
this.useUnseen = useUnseen;
|
||||
this.executeInOrder = executeInOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,6 +161,15 @@ public final class WiredStack {
|
||||
return useUnseen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if ordered execution mode is enabled (WiredExtraExecuteInOrder).
|
||||
* When true, all regular effects execute in stable stack order.
|
||||
* @return true if ordered execution is enabled
|
||||
*/
|
||||
public boolean executeInOrder() {
|
||||
return executeInOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of conditions.
|
||||
* @return condition count
|
||||
@@ -183,6 +196,7 @@ public final class WiredStack {
|
||||
", orMode=" + useOrMode +
|
||||
", random=" + useRandom +
|
||||
", unseen=" + useUnseen +
|
||||
", executeInOrder=" + executeInOrder +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.eu.habbo.habbohotel.wired.core;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredCondition;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraExecuteInOrder;
|
||||
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;
|
||||
@@ -176,6 +177,7 @@ public final class RoomWiredStackIndex implements WiredStackIndex {
|
||||
boolean useOrMode = specialTypes.hasExtraType(x, y, WiredExtraOrEval.class);
|
||||
boolean useRandom = specialTypes.hasExtraType(x, y, WiredExtraRandom.class);
|
||||
boolean useUnseen = specialTypes.hasExtraType(x, y, WiredExtraUnseen.class);
|
||||
boolean executeInOrder = specialTypes.hasExtraType(x, y, WiredExtraExecuteInOrder.class);
|
||||
|
||||
return new WiredStack(
|
||||
trigger,
|
||||
@@ -184,7 +186,8 @@ public final class RoomWiredStackIndex implements WiredStackIndex {
|
||||
effects,
|
||||
useOrMode,
|
||||
useRandom,
|
||||
useUnseen
|
||||
useUnseen,
|
||||
executeInOrder
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterFurni;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraFilterUser;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraExecutionLimit;
|
||||
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.InteractionWiredTrigger;
|
||||
@@ -179,11 +180,11 @@ public final class WiredEngine {
|
||||
|
||||
boolean anyTriggered = false;
|
||||
boolean suppressSaysOutput = false;
|
||||
long currentTime = System.currentTimeMillis();
|
||||
long triggerTime = event.getCreatedAtMs();
|
||||
|
||||
for (WiredStack stack : stacks) {
|
||||
try {
|
||||
boolean triggered = processStack(stack, event, currentTime);
|
||||
boolean triggered = processStack(stack, event, triggerTime);
|
||||
if (triggered) {
|
||||
anyTriggered = true;
|
||||
|
||||
@@ -257,6 +258,15 @@ public final class WiredEngine {
|
||||
debug(room, "No conditions in stack, proceeding to effects");
|
||||
}
|
||||
|
||||
WiredExtraExecutionLimit executionLimitExtra = getExecutionLimitExtra(room, stack);
|
||||
if (executionLimitExtra != null && !executionLimitExtra.tryAcquireExecutionSlot(currentTime)) {
|
||||
debug(room, "Execution limit blocked stack {} (max {} in {} ms)",
|
||||
stack.triggerItem() != null ? stack.triggerItem().getId() : "null",
|
||||
executionLimitExtra.getMaxExecutions(),
|
||||
executionLimitExtra.getTimeWindowMs());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fire plugin event (WiredStackTriggeredEvent)
|
||||
if (!fireTriggeredEvent(stack, event)) {
|
||||
debug(room, "Stack cancelled by plugin");
|
||||
@@ -427,6 +437,10 @@ public final class WiredEngine {
|
||||
debug(ctx.room(), "Unseen mode fallback: selected effect {}/{}", index + 1, regulars.size());
|
||||
}
|
||||
}
|
||||
} else if (stack.executeInOrder()) {
|
||||
debug(ctx.room(), "Ordered mode: executing effect batches in stack order by delay");
|
||||
executeOrderedEffects(regulars, ctx, currentTime);
|
||||
return;
|
||||
} else {
|
||||
// Normal mode: regular effects in random order
|
||||
toExecute = new ArrayList<>(regulars);
|
||||
@@ -569,9 +583,11 @@ public final class WiredEngine {
|
||||
/**
|
||||
* Schedule a delayed effect execution.
|
||||
*/
|
||||
private void scheduleDelayedEffect(IWiredEffect effect, WiredContext ctx, int delay, long currentTime) {
|
||||
private void scheduleDelayedEffect(IWiredEffect effect, WiredContext ctx, int delay, long triggerTime) {
|
||||
// Delay is in 500ms ticks
|
||||
long delayMs = delay * 500L;
|
||||
long elapsedSinceTrigger = Math.max(0L, System.currentTimeMillis() - triggerTime);
|
||||
long remainingDelayMs = Math.max(0L, delayMs - elapsedSinceTrigger);
|
||||
Room room = ctx.room();
|
||||
RoomUnit actor = ctx.actor().orElse(null);
|
||||
|
||||
@@ -592,7 +608,80 @@ public final class WiredEngine {
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Error executing delayed effect: {}", e.getMessage());
|
||||
}
|
||||
}, delayMs);
|
||||
}, remainingDelayMs);
|
||||
}
|
||||
|
||||
private void executeOrderedEffects(List<IWiredEffect> effects, WiredContext ctx, long currentTime) {
|
||||
if (effects == null || effects.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<Integer, List<IWiredEffect>> effectsByDelay = new LinkedHashMap<>();
|
||||
|
||||
for (IWiredEffect effect : effects) {
|
||||
if (effect == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (effect.requiresActor() && !ctx.hasActor()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
effectsByDelay.computeIfAbsent(effect.getDelay(), key -> new ArrayList<>()).add(effect);
|
||||
}
|
||||
|
||||
for (Map.Entry<Integer, List<IWiredEffect>> entry : effectsByDelay.entrySet()) {
|
||||
int delay = entry.getKey();
|
||||
List<IWiredEffect> batch = entry.getValue();
|
||||
|
||||
if (batch.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (delay > 0) {
|
||||
scheduleOrderedEffectBatch(batch, ctx, delay, currentTime);
|
||||
} else {
|
||||
executeOrderedEffectBatch(batch, ctx, currentTime, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleOrderedEffectBatch(List<IWiredEffect> batch, WiredContext ctx, int delay, long triggerTime) {
|
||||
long delayMs = delay * 500L;
|
||||
long elapsedSinceTrigger = Math.max(0L, System.currentTimeMillis() - triggerTime);
|
||||
long remainingDelayMs = Math.max(0L, delayMs - elapsedSinceTrigger);
|
||||
Room room = ctx.room();
|
||||
|
||||
Emulator.getThreading().run(() -> {
|
||||
if (!room.isLoaded() || room.getHabbos().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
executeOrderedEffectBatch(batch, ctx, System.currentTimeMillis(), true);
|
||||
}, remainingDelayMs);
|
||||
}
|
||||
|
||||
private void executeOrderedEffectBatch(List<IWiredEffect> batch, WiredContext ctx, long executionTime, boolean useExecutionTimeForCooldown) {
|
||||
Room room = ctx.room();
|
||||
RoomUnit actor = ctx.actor().orElse(null);
|
||||
|
||||
for (IWiredEffect effect : batch) {
|
||||
try {
|
||||
if (!useExecutionTimeForCooldown) {
|
||||
ctx.state().step();
|
||||
}
|
||||
|
||||
effect.execute(ctx);
|
||||
|
||||
if (effect instanceof InteractionWiredEffect) {
|
||||
InteractionWiredEffect wiredEffect = (InteractionWiredEffect) effect;
|
||||
wiredEffect.setCooldown(executionTime);
|
||||
wiredEffect.activateBox(room, actor, executionTime);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Error executing ordered effect batch item: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -714,6 +803,12 @@ public final class WiredEngine {
|
||||
return (extra instanceof WiredExtraUnseen) ? (WiredExtraUnseen) extra : null;
|
||||
}
|
||||
|
||||
private WiredExtraExecutionLimit getExecutionLimitExtra(Room room, WiredStack stack) {
|
||||
InteractionWiredExtra extra = getStackExtra(room, stack, WiredExtraExecutionLimit.class);
|
||||
|
||||
return (extra instanceof WiredExtraExecutionLimit) ? (WiredExtraExecutionLimit) extra : null;
|
||||
}
|
||||
|
||||
private <T extends InteractionWiredExtra> InteractionWiredExtra getStackExtra(Room room, WiredStack stack, Class<T> extraClass) {
|
||||
if (room == null || stack == null || stack.triggerItem() == null || room.getRoomSpecialTypes() == null) {
|
||||
return null;
|
||||
|
||||
@@ -4,8 +4,10 @@ import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.catalog.CatalogItem;
|
||||
import com.eu.habbo.habbohotel.items.Item;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
|
||||
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.effects.WiredEffectGiveReward;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.effects.WiredEffectTriggerStacks;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraExecutionLimit;
|
||||
import com.eu.habbo.habbohotel.items.interactions.wired.triggers.WiredTriggerHabboClicksUser;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomTile;
|
||||
@@ -725,6 +727,34 @@ public final class WiredManager {
|
||||
return WiredTickService.getInstance();
|
||||
}
|
||||
|
||||
public static boolean isTriggerExecutionAllowed(Room room, HabboItem triggerItem, long timestamp) {
|
||||
WiredExtraExecutionLimit executionLimit = getExecutionLimitExtra(room, triggerItem);
|
||||
|
||||
return executionLimit == null || executionLimit.canExecuteAt(timestamp);
|
||||
}
|
||||
|
||||
public static WiredExtraExecutionLimit getExecutionLimitExtra(Room room, HabboItem triggerItem) {
|
||||
if (room == null || triggerItem == null || room.getRoomSpecialTypes() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
THashSet<InteractionWiredExtra> extras = room.getRoomSpecialTypes().getExtras(
|
||||
triggerItem.getX(),
|
||||
triggerItem.getY());
|
||||
|
||||
if (extras == null || extras.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (InteractionWiredExtra extra : extras) {
|
||||
if (extra instanceof WiredExtraExecutionLimit) {
|
||||
return (WiredExtraExecutionLimit) extra;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// ========== Timer Management ==========
|
||||
|
||||
/**
|
||||
|
||||
+357
-6
@@ -14,6 +14,7 @@ import com.eu.habbo.habbohotel.rooms.RoomUnitStatus;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomUnitType;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboItem;
|
||||
import com.eu.habbo.messages.ServerMessage;
|
||||
import com.eu.habbo.messages.outgoing.rooms.WiredMovementsComposer;
|
||||
import com.eu.habbo.messages.outgoing.rooms.items.FloorItemOnRollerComposer;
|
||||
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
|
||||
@@ -21,16 +22,21 @@ import gnu.trove.set.hash.THashSet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public final class WiredMoveCarryHelper {
|
||||
private static final double DIRECT_HEIGHT_TOLERANCE = 0.1D;
|
||||
private static final int STATUS_SUPPRESSION_GRACE_MS = 250;
|
||||
private static final long USER_FOLLOWER_TTL_MS = 10000L;
|
||||
private static final ThreadLocal<Set<Integer>> SUPPRESSED_STATUS_ROOM_UNIT_IDS = new ThreadLocal<>();
|
||||
private static final ThreadLocal<List<WiredMovementsComposer.MovementData>> COLLECTED_MOVEMENTS = new ThreadLocal<>();
|
||||
private static final ConcurrentHashMap<Integer, Long> SUPPRESSED_STATUS_COMPOSER_UNTIL = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<Integer, ConcurrentHashMap<Integer, UserFollowEntry>> ACTIVE_USER_FOLLOWERS = new ConcurrentHashMap<>();
|
||||
|
||||
private WiredMoveCarryHelper() {
|
||||
}
|
||||
@@ -60,10 +66,22 @@ public final class WiredMoveCarryHelper {
|
||||
}
|
||||
|
||||
public static FurnitureMovementError moveFurni(Room room, HabboItem stackItem, HabboItem movingItem, RoomTile targetTile, int rotation, Habbo actor, boolean sendUpdates, WiredContext ctx) {
|
||||
return moveFurni(room, stackItem, movingItem, targetTile, rotation, null, actor, sendUpdates, ctx);
|
||||
return moveFurni(room, stackItem, movingItem, targetTile, rotation, null, actor, sendUpdates, ctx, null, null, WiredMovementsComposer.FURNI_ANCHOR_NONE, 0);
|
||||
}
|
||||
|
||||
public static FurnitureMovementError moveFurni(Room room, HabboItem stackItem, HabboItem movingItem, RoomTile targetTile, int rotation, Double z, Habbo actor, boolean sendUpdates, WiredContext ctx) {
|
||||
return moveFurni(room, stackItem, movingItem, targetTile, rotation, z, actor, sendUpdates, ctx, null, null, WiredMovementsComposer.FURNI_ANCHOR_NONE, 0);
|
||||
}
|
||||
|
||||
public static FurnitureMovementError moveFurni(Room room, HabboItem stackItem, HabboItem movingItem, RoomTile targetTile, int rotation, Double z, Habbo actor, boolean sendUpdates, WiredContext ctx, Integer animationDurationOverride) {
|
||||
return moveFurni(room, stackItem, movingItem, targetTile, rotation, z, actor, sendUpdates, ctx, animationDurationOverride, null, WiredMovementsComposer.FURNI_ANCHOR_NONE, 0);
|
||||
}
|
||||
|
||||
public static FurnitureMovementError moveFurni(Room room, HabboItem stackItem, HabboItem movingItem, RoomTile targetTile, int rotation, Double z, Habbo actor, boolean sendUpdates, WiredContext ctx, Integer animationDurationOverride, Integer animationElapsedOverride) {
|
||||
return moveFurni(room, stackItem, movingItem, targetTile, rotation, z, actor, sendUpdates, ctx, animationDurationOverride, animationElapsedOverride, WiredMovementsComposer.FURNI_ANCHOR_NONE, 0);
|
||||
}
|
||||
|
||||
public static FurnitureMovementError moveFurni(Room room, HabboItem stackItem, HabboItem movingItem, RoomTile targetTile, int rotation, Double z, Habbo actor, boolean sendUpdates, WiredContext ctx, Integer animationDurationOverride, Integer animationElapsedOverride, int anchorType, int anchorId) {
|
||||
if (room == null || movingItem == null || targetTile == null) {
|
||||
return FurnitureMovementError.INVALID_MOVE;
|
||||
}
|
||||
@@ -97,7 +115,9 @@ public final class WiredMoveCarryHelper {
|
||||
}
|
||||
|
||||
boolean useWiredMovements = !hasNoAnimationExtra(room, stackItem);
|
||||
int animationDuration = getAnimationDuration(room, stackItem, WiredMovementsComposer.DEFAULT_DURATION);
|
||||
int animationDuration = animationDurationOverride != null
|
||||
? Math.max(50, animationDurationOverride)
|
||||
: getAnimationDuration(room, stackItem, WiredMovementsComposer.DEFAULT_DURATION);
|
||||
Set<Integer> previousSuppressedRoomUnitIds = SUPPRESSED_STATUS_ROOM_UNIT_IDS.get();
|
||||
|
||||
if (carryContext.active) {
|
||||
@@ -133,7 +153,7 @@ public final class WiredMoveCarryHelper {
|
||||
if (!useWiredMovements) {
|
||||
applyInstantCarryState(room, movingItem, targetTile, rotation, carryContext);
|
||||
} else if (oldLocation != null) {
|
||||
sendAnimatedMove(room, movingItem, oldLocation, oldZ, targetTile, rotation, carryContext, animationDuration);
|
||||
sendAnimatedMove(room, movingItem, oldLocation, oldZ, targetTile, rotation, carryContext, animationDuration, (animationElapsedOverride != null) ? Math.max(0, animationElapsedOverride) : 0, anchorType, anchorId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,6 +218,165 @@ public final class WiredMoveCarryHelper {
|
||||
SUPPRESSED_STATUS_COMPOSER_UNTIL.remove(roomUnit.getId());
|
||||
}
|
||||
|
||||
public static void beginMovementCollection() {
|
||||
COLLECTED_MOVEMENTS.set(new ArrayList<>());
|
||||
}
|
||||
|
||||
public static ServerMessage finishMovementCollection() {
|
||||
List<WiredMovementsComposer.MovementData> movements = COLLECTED_MOVEMENTS.get();
|
||||
COLLECTED_MOVEMENTS.remove();
|
||||
|
||||
if (movements == null || movements.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new WiredMovementsComposer(movements).compose();
|
||||
}
|
||||
|
||||
public static void registerUserFollower(Room room, HabboItem stackItem, HabboItem movingItem, RoomUnit targetUnit, Double zOverride, WiredContext ctx) {
|
||||
if (room == null || stackItem == null || movingItem == null || targetUnit == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ACTIVE_USER_FOLLOWERS
|
||||
.computeIfAbsent(targetUnit.getId(), key -> new ConcurrentHashMap<>())
|
||||
.compute(movingItem.getId(), (key, existing) -> {
|
||||
if (existing != null
|
||||
&& existing.roomId == room.getId()
|
||||
&& existing.stackItemId == stackItem.getId()) {
|
||||
if (existing.zOverride == null && zOverride != null) {
|
||||
existing.zOverride = zOverride;
|
||||
}
|
||||
existing.ctx = ctx;
|
||||
existing.touch();
|
||||
return existing;
|
||||
}
|
||||
|
||||
return new UserFollowEntry(
|
||||
room.getId(),
|
||||
stackItem.getId(),
|
||||
movingItem.getId(),
|
||||
zOverride,
|
||||
ctx);
|
||||
});
|
||||
}
|
||||
|
||||
public static void markUserFollowerProcessed(RoomUnit targetUnit, HabboItem movingItem, long moveStatusTimestamp) {
|
||||
if (targetUnit == null || movingItem == null || moveStatusTimestamp <= 0L) {
|
||||
return;
|
||||
}
|
||||
|
||||
ConcurrentHashMap<Integer, UserFollowEntry> followers = ACTIVE_USER_FOLLOWERS.get(targetUnit.getId());
|
||||
if (followers == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
UserFollowEntry entry = followers.get(movingItem.getId());
|
||||
if (entry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
entry.markProcessed(moveStatusTimestamp);
|
||||
}
|
||||
|
||||
public static boolean isUserFollowerProcessed(RoomUnit targetUnit, HabboItem movingItem, long moveStatusTimestamp) {
|
||||
if (targetUnit == null || movingItem == null || moveStatusTimestamp <= 0L) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ConcurrentHashMap<Integer, UserFollowEntry> followers = ACTIVE_USER_FOLLOWERS.get(targetUnit.getId());
|
||||
if (followers == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UserFollowEntry entry = followers.get(movingItem.getId());
|
||||
if (entry == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return entry.lastProcessedMoveTimestamp == moveStatusTimestamp;
|
||||
}
|
||||
|
||||
public static void processUserFollowers(Room room, Collection<RoomUnit> roomUnits) {
|
||||
if (room == null || roomUnits == null || roomUnits.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (RoomUnit roomUnit : roomUnits) {
|
||||
if (roomUnit == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ConcurrentHashMap<Integer, UserFollowEntry> followers = ACTIVE_USER_FOLLOWERS.get(roomUnit.getId());
|
||||
if (followers == null || followers.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!roomUnit.hasStatus(RoomUnitStatus.MOVE) || roomUnit.getCurrentLocation() == null) {
|
||||
ACTIVE_USER_FOLLOWERS.remove(roomUnit.getId(), followers);
|
||||
continue;
|
||||
}
|
||||
|
||||
long moveStatusTimestamp = roomUnit.getMoveStatusTimestamp();
|
||||
List<Integer> toRemove = new ArrayList<>();
|
||||
|
||||
if (shouldSettleFollowersForNewStep(followers, moveStatusTimestamp)) {
|
||||
settleUserFollowers(room, followers);
|
||||
}
|
||||
|
||||
List<Map.Entry<Integer, UserFollowEntry>> orderedFollowers = new ArrayList<>(followers.entrySet());
|
||||
orderedFollowers.sort(Comparator
|
||||
.comparingDouble((Map.Entry<Integer, UserFollowEntry> followerEntry) -> {
|
||||
UserFollowEntry entry = followerEntry.getValue();
|
||||
return (entry != null && entry.zOverride != null) ? entry.zOverride : Double.MAX_VALUE;
|
||||
})
|
||||
.thenComparingInt(Map.Entry::getKey));
|
||||
|
||||
for (Map.Entry<Integer, UserFollowEntry> followerEntry : orderedFollowers) {
|
||||
UserFollowEntry entry = followerEntry.getValue();
|
||||
|
||||
if (entry == null || entry.roomId != room.getId() || entry.expiresAt < System.currentTimeMillis()) {
|
||||
toRemove.add(followerEntry.getKey());
|
||||
continue;
|
||||
}
|
||||
|
||||
HabboItem stackItem = room.getHabboItem(entry.stackItemId);
|
||||
HabboItem movingItem = room.getHabboItem(entry.movingItemId);
|
||||
|
||||
if (stackItem == null || movingItem == null) {
|
||||
toRemove.add(followerEntry.getKey());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (moveStatusTimestamp <= 0L || moveStatusTimestamp == entry.lastProcessedMoveTimestamp) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int animationElapsed = resolveMoveStepElapsed(roomUnit);
|
||||
int animationDuration = resolveMoveStepDuration(roomUnit);
|
||||
Double targetZ = resolveFollowerStackZ(room, movingItem, roomUnit.getCurrentLocation(), movingItem.getRotation());
|
||||
FurnitureMovementError error = moveFurni(room, stackItem, movingItem, roomUnit.getCurrentLocation(), movingItem.getRotation(), targetZ, null, false, entry.ctx, animationDuration, animationElapsed, WiredMovementsComposer.FURNI_ANCHOR_USER, roomUnit.getId());
|
||||
|
||||
if (error != FurnitureMovementError.NONE && entry.zOverride != null) {
|
||||
error = moveFurni(room, stackItem, movingItem, roomUnit.getCurrentLocation(), movingItem.getRotation(), entry.zOverride, null, false, entry.ctx, animationDuration, animationElapsed, WiredMovementsComposer.FURNI_ANCHOR_USER, roomUnit.getId());
|
||||
}
|
||||
|
||||
if (error == FurnitureMovementError.INVALID_MOVE) {
|
||||
toRemove.add(followerEntry.getKey());
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.markProcessed(moveStatusTimestamp);
|
||||
}
|
||||
|
||||
for (Integer movingItemId : toRemove) {
|
||||
followers.remove(movingItemId);
|
||||
}
|
||||
|
||||
purgeExpiredFollowers(roomUnit.getId(), followers, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasNoAnimationExtra(Room room, HabboItem stackItem) {
|
||||
return getNoAnimationExtra(room, stackItem) != null;
|
||||
}
|
||||
@@ -207,6 +386,141 @@ public final class WiredMoveCarryHelper {
|
||||
return (extra != null) ? extra.getDurationMs() : fallbackDuration;
|
||||
}
|
||||
|
||||
public static int resolveMoveStepElapsed(RoomUnit roomUnit) {
|
||||
if (roomUnit == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long moveStatusTimestamp = roomUnit.getMoveStatusTimestamp();
|
||||
if (moveStatusTimestamp <= 0L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) Math.max(0L, Math.min(WiredMovementsComposer.DEFAULT_DURATION, System.currentTimeMillis() - moveStatusTimestamp));
|
||||
}
|
||||
|
||||
public static int resolveMoveStepDuration(RoomUnit roomUnit) {
|
||||
return WiredMovementsComposer.DEFAULT_DURATION;
|
||||
}
|
||||
|
||||
public static Double resolveFollowerStackZ(Room room, HabboItem movingItem, RoomTile targetTile, int rotation) {
|
||||
if (room == null || movingItem == null || targetTile == null || room.getLayout() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
double targetZ = room.getStackHeight(targetTile.x, targetTile.y, false, movingItem);
|
||||
THashSet<RoomTile> occupiedTiles = room.getLayout().getTilesAt(
|
||||
targetTile,
|
||||
movingItem.getBaseItem().getWidth(),
|
||||
movingItem.getBaseItem().getLength(),
|
||||
rotation);
|
||||
|
||||
if (occupiedTiles == null || occupiedTiles.isEmpty()) {
|
||||
return targetZ;
|
||||
}
|
||||
|
||||
for (RoomTile occupiedTile : occupiedTiles) {
|
||||
if (occupiedTile == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
targetZ = Math.max(targetZ, room.getStackHeight(occupiedTile.x, occupiedTile.y, false, movingItem));
|
||||
}
|
||||
|
||||
return targetZ;
|
||||
}
|
||||
|
||||
private static Integer resolveRemainingMoveDuration(RoomUnit roomUnit, HabboItem stackItem, Room room) {
|
||||
if (roomUnit == null || stackItem == null || room == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
long moveStatusTimestamp = roomUnit.getMoveStatusTimestamp();
|
||||
if (moveStatusTimestamp <= 0L) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int configuredDuration = getAnimationDuration(room, stackItem, WiredMovementsComposer.DEFAULT_DURATION);
|
||||
int remainingStepDuration = (int) Math.max(50L, WiredMovementsComposer.DEFAULT_DURATION - Math.max(0L, System.currentTimeMillis() - moveStatusTimestamp));
|
||||
return Math.min(configuredDuration, remainingStepDuration);
|
||||
}
|
||||
|
||||
private static boolean shouldSettleFollowersForNewStep(ConcurrentHashMap<Integer, UserFollowEntry> followers, long moveStatusTimestamp) {
|
||||
if (followers == null || followers.isEmpty() || moveStatusTimestamp <= 0L) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (UserFollowEntry entry : followers.values()) {
|
||||
if (entry != null && entry.lastProcessedMoveTimestamp > 0L && entry.lastProcessedMoveTimestamp != moveStatusTimestamp) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void settleUserFollowers(Room room, ConcurrentHashMap<Integer, UserFollowEntry> followers) {
|
||||
if (room == null || followers == null || followers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Map.Entry<Integer, UserFollowEntry>> entriesToSettle = new ArrayList<>(followers.entrySet());
|
||||
entriesToSettle.sort(Comparator
|
||||
.comparingDouble((Map.Entry<Integer, UserFollowEntry> followerEntry) -> {
|
||||
UserFollowEntry entry = followerEntry.getValue();
|
||||
return (entry != null && entry.zOverride != null) ? -entry.zOverride : Double.POSITIVE_INFINITY;
|
||||
})
|
||||
.thenComparingInt(Map.Entry::getKey));
|
||||
|
||||
for (Map.Entry<Integer, UserFollowEntry> followerEntry : entriesToSettle) {
|
||||
UserFollowEntry entry = followerEntry.getValue();
|
||||
|
||||
if (entry == null || entry.roomId != room.getId()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HabboItem movingItem = room.getHabboItem(entry.movingItemId);
|
||||
HabboItem stackItem = room.getHabboItem(entry.stackItemId);
|
||||
if (movingItem == null || room.getLayout() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RoomTile currentTile = room.getLayout().getTile(movingItem.getX(), movingItem.getY());
|
||||
if (currentTile == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Double targetZ = (double) room.getLayout().getHeightAtSquare(currentTile.x, currentTile.y);
|
||||
|
||||
if (stackItem != null) {
|
||||
FurnitureMovementError error = moveFurni(room, stackItem, movingItem, currentTile, movingItem.getRotation(), targetZ, null, false, entry.ctx, WiredMovementsComposer.DEFAULT_DURATION, 0, WiredMovementsComposer.FURNI_ANCHOR_NONE, 0);
|
||||
|
||||
if (error == FurnitureMovementError.NONE) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
FurnitureMovementError error = room.moveFurniTo(movingItem, currentTile, movingItem.getRotation(), targetZ, null, true, false);
|
||||
|
||||
if (error != FurnitureMovementError.NONE) {
|
||||
room.moveFurniTo(movingItem, currentTile, movingItem.getRotation(), null, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void purgeExpiredFollowers(int roomUnitId, ConcurrentHashMap<Integer, UserFollowEntry> followers, boolean removeEmpty) {
|
||||
if (followers == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
followers.entrySet().removeIf(entry -> entry.getValue() == null || entry.getValue().expiresAt < now);
|
||||
|
||||
if (removeEmpty && followers.isEmpty()) {
|
||||
ACTIVE_USER_FOLLOWERS.remove(roomUnitId, followers);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasMovementBehaviorExtra(Room room, HabboItem stackItem) {
|
||||
THashSet<InteractionWiredExtra> extras = getMovementExtras(room, stackItem);
|
||||
if (extras == null || extras.isEmpty()) {
|
||||
@@ -370,7 +684,7 @@ public final class WiredMoveCarryHelper {
|
||||
return FurnitureMovementError.NONE;
|
||||
}
|
||||
|
||||
private static void sendAnimatedMove(Room room, HabboItem movingItem, RoomTile oldLocation, double oldZ, RoomTile targetTile, int rotation, CarryContext carryContext, int animationDuration) {
|
||||
private static void sendAnimatedMove(Room room, HabboItem movingItem, RoomTile oldLocation, double oldZ, RoomTile targetTile, int rotation, CarryContext carryContext, int animationDuration, int animationElapsed, int anchorType, int anchorId) {
|
||||
List<CarriedUnitMove> carriedMoves = getCarriedUnitMoves(room, movingItem, targetTile, rotation, carryContext);
|
||||
List<WiredMovementsComposer.MovementData> movements = new ArrayList<>();
|
||||
movements.add(WiredMovementsComposer.furniMovement(
|
||||
@@ -382,7 +696,10 @@ public final class WiredMoveCarryHelper {
|
||||
oldZ,
|
||||
movingItem.getZ(),
|
||||
movingItem.getRotation(),
|
||||
animationDuration));
|
||||
animationDuration,
|
||||
animationElapsed,
|
||||
anchorType,
|
||||
anchorId));
|
||||
|
||||
for (CarriedUnitMove carriedMove : carriedMoves) {
|
||||
suppressStatusComposer(carriedMove.roomUnit, animationDuration);
|
||||
@@ -399,7 +716,13 @@ public final class WiredMoveCarryHelper {
|
||||
animationDuration));
|
||||
}
|
||||
|
||||
room.sendComposer(new WiredMovementsComposer(movements).compose());
|
||||
List<WiredMovementsComposer.MovementData> collectedMovements = COLLECTED_MOVEMENTS.get();
|
||||
|
||||
if (collectedMovements != null) {
|
||||
collectedMovements.addAll(movements);
|
||||
} else {
|
||||
room.sendComposer(new WiredMovementsComposer(movements).compose());
|
||||
}
|
||||
|
||||
for (CarriedUnitMove carriedMove : carriedMoves) {
|
||||
updateCarriedUnitState(carriedMove);
|
||||
@@ -711,4 +1034,32 @@ public final class WiredMoveCarryHelper {
|
||||
this.newZ = newZ;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class UserFollowEntry {
|
||||
private final int roomId;
|
||||
private final int stackItemId;
|
||||
private final int movingItemId;
|
||||
private Double zOverride;
|
||||
private WiredContext ctx;
|
||||
private long expiresAt;
|
||||
private long lastProcessedMoveTimestamp;
|
||||
|
||||
private UserFollowEntry(int roomId, int stackItemId, int movingItemId, Double zOverride, WiredContext ctx) {
|
||||
this.roomId = roomId;
|
||||
this.stackItemId = stackItemId;
|
||||
this.movingItemId = movingItemId;
|
||||
this.zOverride = zOverride;
|
||||
this.ctx = ctx;
|
||||
this.touch();
|
||||
}
|
||||
|
||||
private void markProcessed(long moveStatusTimestamp) {
|
||||
this.lastProcessedMoveTimestamp = moveStatusTimestamp;
|
||||
this.touch();
|
||||
}
|
||||
|
||||
private void touch() {
|
||||
this.expiresAt = System.currentTimeMillis() + USER_FOLLOWER_TTL_MS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import java.util.List;
|
||||
|
||||
public final class WiredSourceUtil {
|
||||
public static final int SOURCE_TRIGGER = 0;
|
||||
public static final int SOURCE_CLICKED_USER = 11;
|
||||
public static final int SOURCE_SELECTED = 100;
|
||||
public static final int SOURCE_SELECTOR = 200;
|
||||
public static final int SOURCE_SIGNAL = 201;
|
||||
@@ -54,6 +55,11 @@ public final class WiredSourceUtil {
|
||||
switch (sourceType) {
|
||||
case SOURCE_TRIGGER:
|
||||
return ctx.actor().map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
case SOURCE_CLICKED_USER:
|
||||
if (ctx.eventType() == WiredEvent.Type.USER_CLICKS_USER) {
|
||||
return ctx.event().getTargetUnit().map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
case SOURCE_SELECTED:
|
||||
return (selectedUsers != null) ? new ArrayList<>(selectedUsers) : Collections.emptyList();
|
||||
case SOURCE_SELECTOR:
|
||||
@@ -71,6 +77,22 @@ public final class WiredSourceUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isDefaultUserSource(int value) {
|
||||
switch (value) {
|
||||
case SOURCE_TRIGGER:
|
||||
case SOURCE_CLICKED_USER:
|
||||
case SOURCE_SELECTOR:
|
||||
case SOURCE_SIGNAL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSelectableUserSource(int value) {
|
||||
return value == SOURCE_SELECTED || isDefaultUserSource(value);
|
||||
}
|
||||
|
||||
private static WiredTargets getSelectorTargets(WiredContext ctx) {
|
||||
if (ctx == null) {
|
||||
return new WiredTargets();
|
||||
|
||||
+24
-3
@@ -13,6 +13,10 @@ public class WiredMovementsComposer extends MessageComposer {
|
||||
public static final int TYPE_WALL_ITEM_MOVE = 2;
|
||||
public static final int TYPE_USER_DIRECTION = 3;
|
||||
|
||||
public static final int FURNI_ANCHOR_NONE = 0;
|
||||
public static final int FURNI_ANCHOR_USER = 1;
|
||||
public static final int FURNI_ANCHOR_FURNI = 2;
|
||||
|
||||
public static final int USER_MOVEMENT_WALK = 0;
|
||||
public static final int USER_MOVEMENT_SLIDE = 1;
|
||||
public static final int DEFAULT_DURATION = 500;
|
||||
@@ -37,11 +41,19 @@ public class WiredMovementsComposer extends MessageComposer {
|
||||
}
|
||||
|
||||
public static MovementData furniMovement(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ) {
|
||||
return furniMovement(id, fromX, fromY, toX, toY, fromZ, toZ, 0, DEFAULT_DURATION);
|
||||
return furniMovement(id, fromX, fromY, toX, toY, fromZ, toZ, 0, DEFAULT_DURATION, 0, FURNI_ANCHOR_NONE, 0);
|
||||
}
|
||||
|
||||
public static MovementData furniMovement(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ, int rotation, int duration) {
|
||||
return new FurniMovementData(id, fromX, fromY, toX, toY, fromZ, toZ, rotation, duration);
|
||||
return furniMovement(id, fromX, fromY, toX, toY, fromZ, toZ, rotation, duration, 0, FURNI_ANCHOR_NONE, 0);
|
||||
}
|
||||
|
||||
public static MovementData furniMovement(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ, int rotation, int duration, int elapsed) {
|
||||
return furniMovement(id, fromX, fromY, toX, toY, fromZ, toZ, rotation, duration, elapsed, FURNI_ANCHOR_NONE, 0);
|
||||
}
|
||||
|
||||
public static MovementData furniMovement(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ, int rotation, int duration, int elapsed, int anchorType, int anchorId) {
|
||||
return new FurniMovementData(id, fromX, fromY, toX, toY, fromZ, toZ, rotation, duration, elapsed, anchorType, anchorId);
|
||||
}
|
||||
|
||||
public static MovementData userWalkMovement(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ, int bodyDirection, int headDirection, int duration) {
|
||||
@@ -133,8 +145,11 @@ public class WiredMovementsComposer extends MessageComposer {
|
||||
private final int id;
|
||||
private final int rotation;
|
||||
private final int duration;
|
||||
private final int elapsed;
|
||||
private final int anchorType;
|
||||
private final int anchorId;
|
||||
|
||||
private FurniMovementData(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ, int rotation, int duration) {
|
||||
private FurniMovementData(int id, int fromX, int fromY, int toX, int toY, double fromZ, double toZ, int rotation, int duration, int elapsed, int anchorType, int anchorId) {
|
||||
super(TYPE_FURNI_MOVE);
|
||||
this.id = id;
|
||||
this.fromX = fromX;
|
||||
@@ -145,6 +160,9 @@ public class WiredMovementsComposer extends MessageComposer {
|
||||
this.toZ = toZ;
|
||||
this.rotation = rotation;
|
||||
this.duration = duration;
|
||||
this.elapsed = elapsed;
|
||||
this.anchorType = anchorType;
|
||||
this.anchorId = anchorId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -158,6 +176,9 @@ public class WiredMovementsComposer extends MessageComposer {
|
||||
response.appendInt(this.id);
|
||||
response.appendInt(this.rotation);
|
||||
response.appendInt(this.duration);
|
||||
response.appendInt(this.elapsed);
|
||||
response.appendInt(this.anchorType);
|
||||
response.appendInt(this.anchorId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user