diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSendSignal.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSendSignal.java index 460b6936..f9121ce5 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSendSignal.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSendSignal.java @@ -103,30 +103,52 @@ public class WiredEffectSendSignal extends InteractionWiredEffect { } LOGGER.debug("[SendSignal] Resolved {} antenna(s), firing signals", resolvedAntennas.size()); + RoomUnit triggeringUser = ctx.event().getOriginActor().orElseGet(() -> ctx.actor().orElse(null)); List forwardedUsers = WiredSourceUtil.resolveUsersRaw(ctx, this.userForward); List forwardedFurni = WiredSourceUtil.resolveItemsRaw(ctx, this.furniForward, this.forwardItems); - RoomUnit defaultUser = forwardedUsers.isEmpty() ? null : forwardedUsers.get(0); - Collection usersToSend = (signalPerUser && !forwardedUsers.isEmpty()) - ? forwardedUsers - : Collections.singletonList(defaultUser); + List usersToSend; + if (signalPerUser) { + LinkedHashMap mergedUsers = new LinkedHashMap<>(); + + if (triggeringUser != null) { + mergedUsers.put(triggeringUser.getId(), triggeringUser); + } + + for (RoomUnit forwardedUser : forwardedUsers) { + if (forwardedUser == null) { + continue; + } + + mergedUsers.put(forwardedUser.getId(), forwardedUser); + } + + usersToSend = mergedUsers.isEmpty() + ? Collections.singletonList(null) + : new ArrayList<>(mergedUsers.values()); + } else { + usersToSend = Collections.singletonList(triggeringUser); + } Collection furniToSend = !forwardedFurni.isEmpty() ? forwardedFurni : Collections.singletonList(null); int nextDepth = currentDepth + 1; + int signalUserCount = signalPerUser + ? (int) usersToSend.stream().filter(Objects::nonNull).count() + : (!forwardedUsers.isEmpty() ? forwardedUsers.size() : (triggeringUser != null ? 1 : 0)); for (RoomUnit user : usersToSend) { for (HabboItem sourceItem : furniToSend) { for (HabboItem antenna : resolvedAntennas) { - fireSignalAtAntenna(ctx, room, antenna, user, sourceItem, nextDepth); + fireSignalAtAntenna(ctx, room, antenna, user, triggeringUser, sourceItem, signalUserCount, nextDepth); } } } } - private void fireSignalAtAntenna(WiredContext ctx, Room room, HabboItem antenna, RoomUnit actor, HabboItem sourceItem, int depth) { + private void fireSignalAtAntenna(WiredContext ctx, Room room, HabboItem antenna, RoomUnit actor, RoomUnit originActor, HabboItem sourceItem, int signalUserCount, int depth) { if (antenna == null) return; RoomTile tile = room.getLayout().getTile(antenna.getX(), antenna.getY()); if (tile == null) return; @@ -142,12 +164,13 @@ public class WiredEffectSendSignal extends InteractionWiredEffect { .tile(tile) .callStackDepth(depth) .signalChannel(signalChannel) - .signalUserCount(actor != null ? 1 : 0) + .signalUserCount(signalUserCount) .signalFurniCount(sourceItem != null ? 1 : 0) .contextVariableScope(ctx.contextVariables()) .triggeredByEffect(true); if (actor != null) builder.actor(actor); + if (originActor != null) builder.originActor(originActor); if (sourceItem != null) builder.sourceItem(sourceItem); boolean result = dispatchSignalEvent(builder.build()); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java index 0a18ee86..59ad10fd 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java @@ -163,6 +163,7 @@ public final class WiredEvent { private final Type type; private final Room room; private final RoomUnit actor; // nullable - the user/bot that caused the event + private final RoomUnit originActor; // nullable - original user that started the chain, preserved across signals private final HabboItem sourceItem; // nullable - the furniture involved private final RoomTile tile; // nullable - the tile where event occurred private final String text; // nullable - text for say triggers @@ -191,6 +192,7 @@ public final class WiredEvent { this.room = builder.room; this.actor = builder.actor; this.sourceItem = builder.sourceItem; + this.originActor = builder.originActor; this.tile = builder.tile; this.text = builder.text; this.targetUnit = builder.targetUnit; @@ -240,6 +242,17 @@ public final class WiredEvent { return Optional.ofNullable(actor); } + /** + * Get the original actor that started the current event chain. + * For signal events this can differ from {@link #getActor()} when the + * signal forwards users one-by-one but still needs to preserve who + * originally triggered the chain. + * @return optional containing the original actor, or empty if unavailable + */ + public Optional getOriginActor() { + return Optional.ofNullable(originActor); + } + /** * Get the source item that was involved in this event. * For example, the furniture that was clicked or stepped on. @@ -407,6 +420,7 @@ public final class WiredEvent { private final Type type; private final Room room; private RoomUnit actor; + private RoomUnit originActor; private HabboItem sourceItem; private RoomTile tile; private String text; @@ -447,6 +461,11 @@ public final class WiredEvent { return this; } + public Builder originActor(RoomUnit originActor) { + this.originActor = originActor; + return this; + } + /** * Set the source item involved in this event. * @param sourceItem the habbo item diff --git a/docs/wired_send_signal_flow.html b/docs/wired_send_signal_flow.html new file mode 100644 index 00000000..79dd9f69 --- /dev/null +++ b/docs/wired_send_signal_flow.html @@ -0,0 +1,283 @@ + + + + + + Wired Send Signal - Flow Attuale + + + +
+
+ + Wired · Send Signal + +

Schema del flow attuale

+

+ Questa pagina descrive il comportamento attuale del wired send signal, con tutti i casi principali: + antenne, utenti, furni, conteggi e combinazioni finali. È pensata da inoltrare così com'è per un controllo del flow. +

+
+ +
+
+

Formula finale

+

antenne × utenti × furni

+

+ Il numero totale di segnali emessi è dato dal prodotto tra antenne valide, rami utente e rami furni. +

+
+
+

Regola utenti

+

+ Con per ogni utente = disattivo, il ramo usa sempre l'utente che innesca. + Con per ogni utente = attivo, il ramo usa l'utente che innesca più gli utenti trovati dalla source. +

+
+
+

Regola furni

+

+ Se la source furni restituisce elementi, il flow attuale apre un ramo per ogni furni. + Se non restituisce nulla, viene emesso un solo ramo senza furni allegati. +

+
+
+ +
+

Pseudo flow

+
    +
  1. 1. Vengono risolte le antenne destinazione.
  2. +
  3. 2. Restano valide solo le antenne reali; se non ne resta nessuna, il flow si ferma.
  4. +
  5. 3. Viene preso l'utente che ha innescato, se esiste.
  6. +
  7. 4. Vengono risolti gli utenti dalla source utenti.
  8. +
  9. 5. Vengono risolti i furni dalla source furni.
  10. +
  11. 6. Se “per ogni utente” è attivo, si costruisce una lista unica con: +
      +
    • sempre l'utente che innesca, se presente;
    • +
    • poi tutti gli utenti della source;
    • +
    • senza duplicati.
    • +
    +
  12. +
  13. 7. Se “per ogni utente” è disattivo, si usa un solo ramo utente: + l'utente che innesca.
  14. +
  15. 8. Se la source furni ha elementi, si apre un ramo per ogni furni.
  16. +
  17. 9. Se la source furni è vuota, si apre un solo ramo senza furni.
  18. +
  19. 10. Per ogni combinazione antenna + utente + furni viene emesso un segnale separato.
  20. +
  21. 11. Ogni segnale porta con sé: +
      +
    • la tile dell'antenna;
    • +
    • l'utente del ramo corrente, se presente;
    • +
    • l'utente originario che ha innescato la chain, se presente;
    • +
    • il furni del ramo corrente, se presente;
    • +
    • le context variables;
    • +
    • la profondità della chain aggiornata;
    • +
    • i conteggi utenti/furni esposti al ramo ricevente.
    • +
    +
  22. +
+
+ +
+
+

Tabella casi · Utenti

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Per ogni utenteUtente che innescaSource utentiRami utente emessiNota
DisattivoPresenteVuota1Parte sempre con l'utente che innesca.
DisattivoPresente3 utenti1Gli utenti source non diventano rami separati.
DisattivoAssenteVuota1Parte un ramo senza utente.
AttivoPresenteVuota1L'utente che innesca viene sempre incluso.
AttivoPresente3 utenti diversi4Utente che innesca + 3 utenti della source.
AttivoPresenteContiene già l'utente che innescaUtenti uniciNessun duplicato.
AttivoAssente3 utenti3Usa solo gli utenti trovati dalla source.
AttivoAssenteVuota1Parte un ramo senza utente.
+
+
+ +
+

Tabella casi · Furni

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source furniFurni trovatiRami furni emessiDato nel singolo ramo
Vuota01Nessun furni allegato
1 furni11Quel furni
3 furni33Un furni diverso per ramo
7 furni77Un furni diverso per ramo
+
+

+ Nel comportamento attuale, se la source furni restituisce elementi, il flow si apre sempre per furni singolo. +

+
+
+ +
+

Tabella casi · Combinazioni complete

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CasoAntenneRami utenteRami furniTotale segnali
Utente che innesca presente, per ogni utente disattivo, 3 furni2136
Utente che innesca presente, per ogni utente attivo, source utenti con 3 utenti, 3 furni24324
Utente che innesca presente, selector utenti vuoto, 7 furni1177
Nessun utente, source utenti vuota, 7 furni1177
Nessuna antenna valida0qualsiasiqualsiasi0
+
+
+ +
+
+

Conteggi esposti al ricevente

+
    +
  • Conteggio utenti con “per ogni utente” attivo: numero di utenti unici del merge tra utente che innesca e source utenti.
  • +
  • Conteggio utenti con “per ogni utente” disattivo: se la source utenti ha elementi, vale il numero di utenti trovati dalla source; altrimenti vale 1 se esiste l'utente che innesca, altrimenti 0.
  • +
  • Conteggio furni: nel singolo ramo vale 1 se c'è un furni allegato, altrimenti 0.
  • +
+
+
+

Nota importante sul comportamento attuale

+

+ Oggi il flow reale fa fan-out per furni quando la source furni restituisce elementi. Quindi, se dalla source arrivano 7 furni, + il sistema apre 7 rami furni distinti. Questo è importante perché impatta sia il numero totale dei segnali sia i conteggi + osservati a valle. +

+

+ Inoltre il segnale conserva anche l'utente originario che ha avviato la chain, separato dall'utente del ramo corrente. +

+
+
+ + +
+ +