diff --git a/Mage/src/main/java/mage/abilities/Ability.java b/Mage/src/main/java/mage/abilities/Ability.java index ab458969a709..54ff5bc49a5a 100644 --- a/Mage/src/main/java/mage/abilities/Ability.java +++ b/Mage/src/main/java/mage/abilities/Ability.java @@ -34,10 +34,15 @@ public interface Ability extends Controllable, Serializable { /** - * Assigns a new {@link java.util.UUID} + * Assigns a new random {@link java.util.UUID} */ void newId(); + /** + * Assigns a new {@link java.util.UUID} + */ + void newId(UUID newID); + /** * Assigns a new {@link java.util.UUID} */ diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 6da0269961c0..3efeee5af353 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -154,6 +154,18 @@ public void newId() { } } + @Override + public void newId(UUID newID) { + if (!(this instanceof MageSingleton)) { + this.id = newID; + } + getEffects().newId(newID); + + for (Ability sub : getSubAbilities()) { + sub.newId(); + } + } + @Override public void newOriginalId() { this.id = UUID.randomUUID(); diff --git a/Mage/src/main/java/mage/abilities/effects/Effect.java b/Mage/src/main/java/mage/abilities/effects/Effect.java index 60e57c9f8873..62902611c4ce 100644 --- a/Mage/src/main/java/mage/abilities/effects/Effect.java +++ b/Mage/src/main/java/mage/abilities/effects/Effect.java @@ -21,6 +21,8 @@ public interface Effect extends Serializable, Copyable { void newId(); + void newId(UUID newID); + /** * Some general behaviours for rule text handling: Rule text of effects get * automatically a full stop "." at the end, if not another effect e.g. with diff --git a/Mage/src/main/java/mage/abilities/effects/EffectImpl.java b/Mage/src/main/java/mage/abilities/effects/EffectImpl.java index c5a81b5f93c9..d105644765f3 100644 --- a/Mage/src/main/java/mage/abilities/effects/EffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/EffectImpl.java @@ -106,8 +106,13 @@ public TargetPointer getTargetPointer() { @Override public void newId() { + this.newId(UUID.randomUUID()); + } + + @Override + public void newId(UUID newID){ if (!(this instanceof MageSingleton)) { - this.id = UUID.randomUUID(); + this.id = newID; } } diff --git a/Mage/src/main/java/mage/abilities/effects/Effects.java b/Mage/src/main/java/mage/abilities/effects/Effects.java index 2cdc4d54bc50..efb7781739a2 100644 --- a/Mage/src/main/java/mage/abilities/effects/Effects.java +++ b/Mage/src/main/java/mage/abilities/effects/Effects.java @@ -6,8 +6,10 @@ import mage.target.targetpointer.TargetPointer; import mage.util.CardUtil; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; +import java.util.UUID; /** * @author BetaSteward_at_googlemail.com @@ -185,6 +187,20 @@ public void newId() { } } + /** + * Generates new UUIDs for all effects deterministically, using seed ID. + * @param newID ID to use as seed for PRNG + */ + public void newId(UUID newID) { + SecureRandom numberGenerator = new SecureRandom(newID.toString().getBytes()); + long msb, lsb; + for (Effect effect : this) { + msb = numberGenerator.nextLong(); + lsb = numberGenerator.nextLong(); + effect.newId(new UUID(msb, lsb)); + } + } + public void setTargetPointer(TargetPointer targetPointer) { if (targetPointer == null) { return; diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index ab9258fb39e1..ebfb99572db9 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -441,7 +441,15 @@ public Ability addAbility(Ability ability, UUID sourceId, Game game, boolean fro // other abilities -- any amount of instances if (!abilities.containsKey(ability.getId())) { Ability copyAbility = ability.copy(); - copyAbility.newId(); // needed so that source can get an ability multiple times (e.g. Raging Ravine) + + // needed so that source can get an ability multiple times (e.g. Raging Ravine) + // Deterministic to allow repeated ability copies to have same UUID + // (see https://github.com/magefree/mage/issues/10372) + long msb = ability.getId().getMostSignificantBits() ^ getId().getMostSignificantBits(); + long lsb = ability.getId().getLeastSignificantBits() ^ getId().getLeastSignificantBits(); + copyAbility.newId(new UUID(msb, lsb)); +// copyAbility.newId(); + copyAbility.setControllerId(controllerId); copyAbility.setSourceId(objectId); // triggered abilities must be added to the state().triggers diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java index d87f9fdf8713..0c0c90b89e60 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -488,6 +488,11 @@ public void newId() { } } + @Override + public void newId(UUID newID) { + this.ability.newId(newID); + } + @Override public void newOriginalId() { }