From f0166157383ea7af2d054473866cdcf777c0955a Mon Sep 17 00:00:00 2001 From: Vincent Quatrevieux Date: Thu, 15 Feb 2024 18:09:36 +0100 Subject: [PATCH] chore: Optimise allocation on PlayerSprite --- .../game/account/CharacterAccessories.java | 2 +- .../game/exploration/sprite/PlayerSprite.java | 71 ++++++++++++++++--- .../game/fight/castable/BaseCastScope.java | 2 +- .../game/fight/castable/CastScope.java | 2 +- .../handler/misc/RevealInvisibleHandler.java | 4 +- .../accessory/InventoryAccessories.java | 2 +- .../sprite/AbstractPlayerSpriteInfo.java | 2 +- .../game/player/sprite/SpriteSize.java | 33 ++++++++- .../game/spell/effect/area/CellArea.java | 6 +- .../spell/effect/area/CheckboardArea.java | 4 +- .../game/spell/effect/area/CircleArea.java | 6 +- .../game/spell/effect/area/CircularArea.java | 10 ++- .../game/spell/effect/area/CrossArea.java | 9 ++- .../game/spell/effect/area/LineArea.java | 12 ++-- .../effect/area/PerpendicularLineArea.java | 9 ++- .../game/spell/effect/area/RingArea.java | 4 +- .../spell/effect/area/SpellEffectArea.java | 8 ++- .../araknemu/game/spell/effect/area/Util.java | 28 +++++--- .../accessory/AbstractAccessories.java | 35 ++++++++- .../world/creature/accessory/Accessories.java | 11 +-- .../creature/accessory/EmptyAccessories.java | 2 +- .../creature/accessory/NullAccessory.java | 23 +++++- .../game/fight/castable/CastScopeTest.java | 2 +- .../game/player/PlayerSpriteTest.java | 53 ++++++++++++++ .../game/player/sprite/SpriteSizeTest.java | 28 +++++++- .../game/spell/effect/area/CellAreaTest.java | 2 +- .../spell/effect/area/CheckboardAreaTest.java | 2 +- .../spell/effect/area/CircleAreaTest.java | 5 +- .../game/spell/effect/area/CrossAreaTest.java | 5 +- .../game/spell/effect/area/LineAreaTest.java | 5 +- .../area/PerpendicularLineAreaTest.java | 5 +- .../game/spell/effect/area/RingAreaTest.java | 2 +- .../game/spell/effect/area/UtilTest.java | 7 +- .../creature/accessory/NullAccessoryTest.java | 11 ++- 34 files changed, 325 insertions(+), 87 deletions(-) diff --git a/src/main/java/fr/quatrevieux/araknemu/game/account/CharacterAccessories.java b/src/main/java/fr/quatrevieux/araknemu/game/account/CharacterAccessories.java index 9a0f6a888..96465569c 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/account/CharacterAccessories.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/account/CharacterAccessories.java @@ -47,7 +47,7 @@ public CharacterAccessories(Collection items) { public Accessory get(AccessoryType type) { final Accessory accessory = accessories.get(type); - return accessory != null ? accessory : new NullAccessory(type); + return accessory != null ? accessory : NullAccessory.from(type); } private void set(Accessory accessory) { diff --git a/src/main/java/fr/quatrevieux/araknemu/game/exploration/sprite/PlayerSprite.java b/src/main/java/fr/quatrevieux/araknemu/game/exploration/sprite/PlayerSprite.java index e29d28ea7..f7b5afcb7 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/exploration/sprite/PlayerSprite.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/exploration/sprite/PlayerSprite.java @@ -20,8 +20,13 @@ package fr.quatrevieux.araknemu.game.exploration.sprite; import fr.arakne.utils.maps.constant.Direction; +import fr.arakne.utils.value.Colors; +import fr.arakne.utils.value.constant.Gender; +import fr.arakne.utils.value.constant.Race; import fr.quatrevieux.araknemu.game.exploration.ExplorationPlayer; +import fr.quatrevieux.araknemu.game.player.sprite.SpriteSize; import fr.quatrevieux.araknemu.game.world.creature.Sprite; +import org.checkerframework.checker.nullness.qual.Nullable; /** * Sprite for exploration player @@ -33,6 +38,17 @@ public final class PlayerSprite implements Sprite { private final ExplorationPlayer exploration; + /** + * Cached constant part of the sprite + * The integrity of this field is ensured by the constantPartHash field + */ + private @Nullable String constantPart = null; + + /** + * Store a hash code of the last computed constant part + */ + private int constPartHash = 0; + public PlayerSprite(ExplorationPlayer exploration) { this.exploration = exploration; } @@ -75,19 +91,56 @@ public String toString() { return cell() + ";" + orientation().ordinal() + ";" + - "0;" + // bonus - id() + ";" + - name() + ";" + - exploration.player().spriteInfo().race().ordinal() + ";" + // @todo title - gfxId() + "^" + exploration.player().spriteInfo().size() + ";" + - exploration.player().spriteInfo().gender().ordinal() + ";" + + constantPart() + ; + } + + /** + * Get the part of the sprite which change rarely + * This value is cached to avoid recomputation and allocation + */ + private String constantPart() { + final int id = id(); + final String name = name(); + final Race race = exploration.player().spriteInfo().race(); + final int gfxId = gfxId(); + final SpriteSize size = exploration.player().spriteInfo().size(); + final Gender gender = exploration.player().spriteInfo().gender(); + final Colors colors = exploration.player().spriteInfo().colors(); + final String accessories = exploration.player().spriteInfo().accessories().toString(); + final int restrictions = exploration.restrictions().toInt(); + final String lastResult = constantPart; + + int newHash = 17 * id; + + newHash = 31 * newHash + name.hashCode(); + newHash = 31 * newHash + race.hashCode(); + newHash = 31 * newHash + gfxId; + newHash = 31 * newHash + size.hashCode(); + newHash = 31 * newHash + gender.hashCode(); + newHash = 31 * newHash + colors.hashCode(); + newHash = 31 * newHash + accessories.hashCode(); + newHash = 31 * newHash + restrictions; + + if (lastResult != null && newHash == constPartHash) { + return lastResult; + } + + constPartHash = newHash; + + return constantPart = "0;" + // bonus + id + ";" + + name + ";" + + race.ordinal() + ";" + // @todo title + gfxId + "^" + size + ";" + + gender.ordinal() + ";" + ";" + // @todo alignment - exploration.player().spriteInfo().colors().toHexString(";") + ";" + - exploration.player().spriteInfo().accessories() + ";" + + colors.toHexString(";") + ";" + + accessories + ";" + ";" + // @todo aura ";;" + // @todo emote; emote timer ";;" + // @todo guild; guild emblem - Integer.toString(exploration.restrictions().toInt(), 36) + ";" + Integer.toString(restrictions, 36) + ";" // @todo mount ; } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/BaseCastScope.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/BaseCastScope.java index fae67d22f..f975cebe2 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/BaseCastScope.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/BaseCastScope.java @@ -234,7 +234,7 @@ public Collection targets() { } @Override - public Set cells() { + public List cells() { return effect.area().resolve(target, from); } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/CastScope.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/CastScope.java index 84a858bd6..b261e1c2a 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/CastScope.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/CastScope.java @@ -117,6 +117,6 @@ public static interface EffectScope cells(); + public List cells(); } } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/RevealInvisibleHandler.java b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/RevealInvisibleHandler.java index d21513ae4..925becea0 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/RevealInvisibleHandler.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/fight/castable/effect/handler/misc/RevealInvisibleHandler.java @@ -27,7 +27,7 @@ import fr.quatrevieux.araknemu.game.fight.map.FightCell; import java.util.Collection; -import java.util.Set; +import java.util.List; /** * Reveal all invisible objects and fighters @@ -61,7 +61,7 @@ private void revealFighters(Fighter caster, Collection targets) { } private void revealObjects(Fighter caster, FightCastScope.EffectScope effect) { - final Set cells = effect.cells(); + final List cells = effect.cells(); for (BattlefieldObject object : fight.map().objects()) { if (cells.contains(object.cell()) && !object.visible(caster)) { diff --git a/src/main/java/fr/quatrevieux/araknemu/game/player/inventory/accessory/InventoryAccessories.java b/src/main/java/fr/quatrevieux/araknemu/game/player/inventory/accessory/InventoryAccessories.java index 0c2c02a96..a960e7169 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/player/inventory/accessory/InventoryAccessories.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/player/inventory/accessory/InventoryAccessories.java @@ -39,7 +39,7 @@ public InventoryAccessories(InventorySlots slots) { public Accessory get(AccessoryType type) { return slots.get(type.slot()).entry() .map(InventoryAccessory::new) - .orElseGet(() -> new NullAccessory(type)) + .orElseGet(() -> NullAccessory.from(type)) ; } } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/player/sprite/AbstractPlayerSpriteInfo.java b/src/main/java/fr/quatrevieux/araknemu/game/player/sprite/AbstractPlayerSpriteInfo.java index d8b4162db..6fe5c7d3e 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/player/sprite/AbstractPlayerSpriteInfo.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/player/sprite/AbstractPlayerSpriteInfo.java @@ -56,7 +56,7 @@ public int gfxId() { @Override public SpriteSize size() { - return new SpriteSize(100, 100); + return SpriteSize.DEFAULT; } @Override diff --git a/src/main/java/fr/quatrevieux/araknemu/game/player/sprite/SpriteSize.java b/src/main/java/fr/quatrevieux/araknemu/game/player/sprite/SpriteSize.java index 6b5dcdcaf..5ae25db88 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/player/sprite/SpriteSize.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/player/sprite/SpriteSize.java @@ -19,12 +19,17 @@ package fr.quatrevieux.araknemu.game.player.sprite; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Store size of the sprite */ public final class SpriteSize { + public static final SpriteSize DEFAULT = new SpriteSize(100, 100); + private final int x; private final int y; + private @Nullable String stringCache = null; public SpriteSize(int x, int y) { this.x = x; @@ -41,6 +46,32 @@ public int y() { @Override public String toString() { - return x + "x" + y; + final String stringCache = this.stringCache; + + if (stringCache != null) { + return stringCache; + } + + return this.stringCache = x + "x" + y; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + final SpriteSize that = (SpriteSize) o; + + return x == that.x && y == that.y; + } + + @Override + public int hashCode() { + return (17 + x) * 31 + y; } } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CellArea.java b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CellArea.java index 516cc4017..f7d713a51 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CellArea.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CellArea.java @@ -24,7 +24,7 @@ import org.checkerframework.checker.index.qual.NonNegative; import java.util.Collections; -import java.util.Set; +import java.util.List; /** * Resolve single cell @@ -33,8 +33,8 @@ public final class CellArea implements SpellEffectArea { public static final CellArea INSTANCE = new CellArea(); @Override - public Set resolve(C target, C source) { - return Collections.singleton(target); + public List resolve(C target, C source) { + return Collections.singletonList(target); } @Override diff --git a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CheckboardArea.java b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CheckboardArea.java index 2147b6cbb..d69376e33 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CheckboardArea.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CheckboardArea.java @@ -23,7 +23,7 @@ import fr.quatrevieux.araknemu.data.value.EffectArea; import org.checkerframework.checker.index.qual.NonNegative; -import java.util.Set; +import java.util.List; /** * Resolve ring area (circle border) @@ -38,7 +38,7 @@ public CheckboardArea(EffectArea area) { } @Override - public Set resolve(C target, C source) { + public List resolve(C target, C source) { return area.resolve(target, source); } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircleArea.java b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircleArea.java index 0c1b58524..2cd11ef68 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircleArea.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircleArea.java @@ -24,7 +24,7 @@ import org.checkerframework.checker.index.qual.NonNegative; import java.util.Collections; -import java.util.Set; +import java.util.List; /** * Resolve circle area @@ -37,12 +37,12 @@ public CircleArea(EffectArea area) { } @Override - public Set resolve(C target, C source) { + public List resolve(C target, C source) { final @NonNegative int size = area.size(); switch (size) { case 0: - return Collections.singleton(target); + return Collections.singletonList(target); case 1: return Util.resolveCenterAndAdjacent(target); diff --git a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircularArea.java b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircularArea.java index 8bc3f3554..c422c9a1b 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircularArea.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircularArea.java @@ -25,7 +25,8 @@ import fr.quatrevieux.araknemu.data.value.EffectArea; import org.checkerframework.checker.index.qual.NonNegative; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; import java.util.function.Predicate; /** @@ -41,10 +42,11 @@ public CircularArea(EffectArea area, Predicate distanceChecker) { } @Override - public Set resolve(C target, C source) { + @SuppressWarnings("unchecked") + public List resolve(C target, C source) { final DofusMap map = target.map(); final CoordinateCell center = target.coordinate(); - final Set cells = Util.createOrderedSet(target); + final List cells = new ArrayList<>(); for (int i = 0; i < map.size(); ++i) { final CoordinateCell cell = map.get(i).coordinate(); @@ -54,6 +56,8 @@ public Set resolve(C target, C source) { } } + Util.sortCells(target, cells); + return cells; } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CrossArea.java b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CrossArea.java index b2e3388de..b6cb1aaae 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CrossArea.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/CrossArea.java @@ -24,7 +24,8 @@ import fr.quatrevieux.araknemu.data.value.EffectArea; import org.checkerframework.checker.index.qual.NonNegative; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; /** * Resolver for cross area @@ -37,13 +38,13 @@ public CrossArea(EffectArea area) { } @Override - public Set resolve(C target, C source) { + public List resolve(C target, C source) { // Optimization for size 1 if (size() == 1) { return Util.resolveCenterAndAdjacent(target); } - final Set cells = Util.createOrderedSet(target); + final List cells = new ArrayList<>(size() * 4 + 1); cells.add(target); @@ -55,6 +56,8 @@ public Set resolve(C target, C source) { LineArea.addCells(cells, target, direction, area.size()); } + Util.sortCells(target, cells); + return cells; } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/LineArea.java b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/LineArea.java index 2ffb50411..1f4c891be 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/LineArea.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/LineArea.java @@ -26,9 +26,9 @@ import fr.quatrevieux.araknemu.data.value.EffectArea; import org.checkerframework.checker.index.qual.NonNegative; +import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.List; /** * Resolve in line area @@ -41,13 +41,13 @@ public LineArea(EffectArea area) { } @Override - public Set resolve(C target, C source) { + public List resolve(C target, C source) { if (area.size() == 0) { - return Collections.singleton(target); + return Collections.singletonList(target); } final Direction direction = source.coordinate().directionTo(target.coordinate()); - final Set cells = new LinkedHashSet<>(area.size() + 1); // Cell are added in order, so no need to sort using a TreeSet + final List cells = new ArrayList<>(area.size() + 1); cells.add(target); addCells(cells, target, direction, area.size()); @@ -65,7 +65,7 @@ public EffectArea.Type type() { return area.size(); } - static > void addCells(Set cells, C start, Direction direction, int size) { + static > void addCells(List cells, C start, Direction direction, int size) { final DofusMap map = start.map(); final int inc = direction.nextCellIncrement(map.dimensions().width()); diff --git a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/PerpendicularLineArea.java b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/PerpendicularLineArea.java index cbd15a190..8c8dc0a55 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/PerpendicularLineArea.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/PerpendicularLineArea.java @@ -24,7 +24,8 @@ import fr.quatrevieux.araknemu.data.value.EffectArea; import org.checkerframework.checker.index.qual.NonNegative; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; /** * Resolve perpendicular line (i.e. baton area) @@ -37,8 +38,8 @@ public PerpendicularLineArea(EffectArea area) { } @Override - public Set resolve(C target, C source) { - final Set cells = Util.createOrderedSet(target); + public List resolve(C target, C source) { + final List cells = new ArrayList<>(1 + 2 * area.size()); final Direction direction = source.coordinate().directionTo(target).orthogonal(); cells.add(target); @@ -46,6 +47,8 @@ public Set resolve(C target, C source) { LineArea.addCells(cells, target, direction, area.size()); LineArea.addCells(cells, target, direction.opposite(), area.size()); + Util.sortCells(target, cells); + return cells; } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/RingArea.java b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/RingArea.java index 3440cc791..cba6634b9 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/RingArea.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/RingArea.java @@ -23,7 +23,7 @@ import fr.quatrevieux.araknemu.data.value.EffectArea; import org.checkerframework.checker.index.qual.NonNegative; -import java.util.Set; +import java.util.List; /** * Resolve ring area (circle border) @@ -36,7 +36,7 @@ public RingArea(EffectArea area) { } @Override - public Set resolve(C target, C source) { + public List resolve(C target, C source) { return area.resolve(target, source); } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/SpellEffectArea.java b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/SpellEffectArea.java index 8603a5b88..6b6975360 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/SpellEffectArea.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/SpellEffectArea.java @@ -23,7 +23,7 @@ import fr.quatrevieux.araknemu.data.value.EffectArea; import org.checkerframework.checker.index.qual.NonNegative; -import java.util.Set; +import java.util.List; /** * Resolve cells from area and target cell @@ -31,12 +31,14 @@ public interface SpellEffectArea { /** * Resolve the cells from an effect area - * Cells must be sorted by distance from the target cell, nearest cells first + * + * Cells must be sorted by distance from the target cell, nearest cells first, + * and then sorted by direction, clockwise from the north-east * * @param target The target cell * @param source The source cell (caster cell) */ - public Set resolve(C target, C source); + public List resolve(C target, C source); /** * The area type diff --git a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/Util.java b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/Util.java index 17f615218..45374acea 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/Util.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/spell/effect/area/Util.java @@ -25,9 +25,8 @@ import fr.arakne.utils.maps.constant.Direction; import org.checkerframework.checker.nullness.qual.Nullable; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.TreeSet; +import java.util.ArrayList; +import java.util.List; /** * Utility methods for resolving effect areas @@ -45,12 +44,20 @@ private Util() { } /** - * Create a set with cells ordered by distance from target cell + * Sort all cells of the given list by distance from the target cell, + * and then by direction, clockwise starting from the north-east + * + * Note: this method will modify the given list + * + * @param target the target cell (i.e. center of the area) + * @param cells the cells to sort + * + * @param the cell type */ - public static Set createOrderedSet(C target) { - final CoordinateCell center = target.coordinate(); + public static void sortCells(C target, List cells) { + final @SuppressWarnings("unchecked") CoordinateCell center = target.coordinate(); - return new TreeSet<>((o1, o2) -> { + cells.sort((o1, o2) -> { final int firstDistance = center.distance(o1); final int secondDistance = center.distance(o2); @@ -71,12 +78,11 @@ public static Set createOrderedSet(C target) { * * @param the cell type */ - @SuppressWarnings("unchecked") - public static Set resolveCenterAndAdjacent(C center) { - final Set set = new LinkedHashSet<>(); + public static List resolveCenterAndAdjacent(C center) { + final List set = new ArrayList<>(5); final int targetId = center.id(); - final DofusMap map = center.map(); + final @SuppressWarnings("unchecked") DofusMap map = center.map(); final int mapWidth = map.dimensions().width(); final int mapSize = map.size(); diff --git a/src/main/java/fr/quatrevieux/araknemu/game/world/creature/accessory/AbstractAccessories.java b/src/main/java/fr/quatrevieux/araknemu/game/world/creature/accessory/AbstractAccessories.java index 37e8b822a..52c79e02f 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/world/creature/accessory/AbstractAccessories.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/world/creature/accessory/AbstractAccessories.java @@ -19,14 +19,43 @@ package fr.quatrevieux.araknemu.game.world.creature.accessory; -import org.apache.commons.lang3.StringUtils; +import java.util.ArrayList; +import java.util.List; /** * Base accessories class */ public abstract class AbstractAccessories implements Accessories { + private static final AccessoryType[] ACCESSORY_TYPES = AccessoryType.values(); + + /** + * Get all accessories + */ + public final List all() { + final List accessories = new ArrayList<>(ACCESSORY_TYPES.length); + + for (AccessoryType type : ACCESSORY_TYPES) { + accessories.add(get(type)); + } + + return accessories; + } + @Override - public String toString() { - return StringUtils.join(all(), ","); + public final String toString() { + final StringBuilder sb = new StringBuilder(); + boolean first = true; + + for (AccessoryType type : ACCESSORY_TYPES) { + if (first) { + first = false; + } else { + sb.append(","); + } + + sb.append(get(type)); + } + + return sb.toString(); } } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/world/creature/accessory/Accessories.java b/src/main/java/fr/quatrevieux/araknemu/game/world/creature/accessory/Accessories.java index 3fe50a413..9c43a7dc6 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/world/creature/accessory/Accessories.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/world/creature/accessory/Accessories.java @@ -19,7 +19,6 @@ package fr.quatrevieux.araknemu.game.world.creature.accessory; -import java.util.ArrayList; import java.util.List; /** @@ -34,13 +33,5 @@ public interface Accessories { /** * Get all accessories */ - public default List all() { - final ArrayList accessories = new ArrayList<>(AccessoryType.values().length); - - for (AccessoryType type : AccessoryType.values()) { - accessories.add(get(type)); - } - - return accessories; - } + public List all(); } diff --git a/src/main/java/fr/quatrevieux/araknemu/game/world/creature/accessory/EmptyAccessories.java b/src/main/java/fr/quatrevieux/araknemu/game/world/creature/accessory/EmptyAccessories.java index fdd4abd78..58ce5a379 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/world/creature/accessory/EmptyAccessories.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/world/creature/accessory/EmptyAccessories.java @@ -28,7 +28,7 @@ public final class EmptyAccessories implements Accessories { @Override public Accessory get(AccessoryType type) { - return new NullAccessory(type); + return NullAccessory.from(type); } @Override diff --git a/src/main/java/fr/quatrevieux/araknemu/game/world/creature/accessory/NullAccessory.java b/src/main/java/fr/quatrevieux/araknemu/game/world/creature/accessory/NullAccessory.java index 65229891f..64aebc75f 100644 --- a/src/main/java/fr/quatrevieux/araknemu/game/world/creature/accessory/NullAccessory.java +++ b/src/main/java/fr/quatrevieux/araknemu/game/world/creature/accessory/NullAccessory.java @@ -19,13 +19,27 @@ package fr.quatrevieux.araknemu.game.world.creature.accessory; +import org.checkerframework.checker.nullness.util.NullnessUtil; + +import java.util.EnumMap; +import java.util.Map; + /** * Null object for accessory */ public final class NullAccessory implements Accessory { + private static final Map INSTANCES; private final AccessoryType type; - public NullAccessory(AccessoryType type) { + static { + INSTANCES = new EnumMap<>(AccessoryType.class); + + for (AccessoryType type : AccessoryType.values()) { + INSTANCES.put(type, new NullAccessory(type)); + } + } + + private NullAccessory(AccessoryType type) { this.type = type; } @@ -43,4 +57,11 @@ public int appearance() { public String toString() { return ""; } + + /** + * Get the null accessory for the given type + */ + public static NullAccessory from(AccessoryType type) { + return NullnessUtil.castNonNull(INSTANCES.get(type)); + } } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/CastScopeTest.java b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/CastScopeTest.java index 406862daf..d597aaca6 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/CastScopeTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/fight/castable/CastScopeTest.java @@ -118,7 +118,7 @@ void withEffectsWillResolveTarget() { assertEquals(effect, scope.effects().get(0).effect()); assertEquals(Collections.singleton(target), scope.effects().get(0).targets()); assertForEachTargetAndDistance(scope.effects().get(0), Pair.of(target, 0)); - assertEquals(Collections.singleton(target.cell()), scope.effects().get(0).cells()); + assertEquals(Collections.singletonList(target.cell()), scope.effects().get(0).cells()); } @Test diff --git a/src/test/java/fr/quatrevieux/araknemu/game/player/PlayerSpriteTest.java b/src/test/java/fr/quatrevieux/araknemu/game/player/PlayerSpriteTest.java index 987c2c64a..a1bd6f018 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/player/PlayerSpriteTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/player/PlayerSpriteTest.java @@ -21,15 +21,21 @@ import fr.arakne.utils.maps.constant.Direction; import fr.quatrevieux.araknemu.core.di.ContainerException; +import fr.quatrevieux.araknemu.data.living.entity.player.Player; import fr.quatrevieux.araknemu.game.GameBaseCase; import fr.quatrevieux.araknemu.game.exploration.ExplorationPlayer; import fr.quatrevieux.araknemu.game.exploration.sprite.PlayerSprite; import fr.quatrevieux.araknemu.game.item.ItemService; import fr.quatrevieux.araknemu.game.item.inventory.exception.InventoryException; +import fr.quatrevieux.araknemu.game.player.inventory.InventoryEntry; +import fr.quatrevieux.araknemu.game.player.inventory.slot.BootsSlot; +import fr.quatrevieux.araknemu.game.player.inventory.slot.HelmetSlot; +import fr.quatrevieux.araknemu.game.player.inventory.slot.WeaponSlot; import fr.quatrevieux.araknemu.game.world.creature.Sprite; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.lang.reflect.Field; import java.sql.SQLException; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -105,4 +111,51 @@ void withOrientation() throws SQLException, ContainerException { new PlayerSprite(exploration).toString() ); } + + @Test + void generateTwiceWithoutChangeConstantPart() throws SQLException { + ExplorationPlayer exploration = new ExplorationPlayer(gamePlayer()); + + assertEquals( + "200;1;0;1;Bob;1;10^100x100;0;;7b;1c8;315;,,,,;;;;;;8;", + new PlayerSprite(exploration).toString() + ); + + exploration.setOrientation(Direction.WEST); + + assertEquals( + "200;4;0;1;Bob;1;10^100x100;0;;7b;1c8;315;,,,,;;;;;;8;", + new PlayerSprite(exploration).toString() + ); + } + + @Test + void shouldRegenerateOnChange() throws SQLException, NoSuchFieldException, IllegalAccessException { + ExplorationPlayer exploration = new ExplorationPlayer(gamePlayer()); + + assertEquals("200;1;0;1;Bob;1;10^100x100;0;;7b;1c8;315;,,,,;;;;;;8;", new PlayerSprite(exploration).toString()); + + exploration.player().restrictions().unset(Restrictions.Restriction.ALLOW_MOVE_ALL_DIRECTION); + exploration.player().restrictions().set(Restrictions.Restriction.DENY_MERCHANT); + exploration.player().restrictions().set(Restrictions.Restriction.DENY_ASSAULT); + exploration.player().restrictions().set(Restrictions.Restriction.DENY_EXCHANGE); + + exploration.restrictions().refresh(); + assertEquals("200;1;0;1;Bob;1;10^100x100;0;;7b;1c8;315;,,,,;;;;;;d;", new PlayerSprite(exploration).toString()); + + exploration.player().inventory() + .add(container.get(ItemService.class).create(2411)) + .move(HelmetSlot.SLOT_ID, 1) + ; + assertEquals("200;1;0;1;Bob;1;10^100x100;0;;7b;1c8;315;,96b,,,;;;;;;d;", new PlayerSprite(exploration).toString()); + + exploration.player().inventory() + .add(container.get(ItemService.class).create(2416)) + .move(WeaponSlot.SLOT_ID, 1) + ; + assertEquals("200;1;0;1;Bob;1;10^100x100;0;;7b;1c8;315;970,96b,,,;;;;;;d;", new PlayerSprite(exploration).toString()); + + exploration.player().inventory().bySlot(HelmetSlot.SLOT_ID).ifPresent(InventoryEntry::unequip); + assertEquals("200;1;0;1;Bob;1;10^100x100;0;;7b;1c8;315;970,,,,;;;;;;d;", new PlayerSprite(exploration).toString()); + } } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/player/sprite/SpriteSizeTest.java b/src/test/java/fr/quatrevieux/araknemu/game/player/sprite/SpriteSizeTest.java index 650c8dd98..5504ec114 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/player/sprite/SpriteSizeTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/player/sprite/SpriteSizeTest.java @@ -22,6 +22,8 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; class SpriteSizeTest { @Test @@ -32,4 +34,28 @@ void values() { assertEquals(120, size.y()); assertEquals("150x120", size.toString()); } -} \ No newline at end of file + + @Test + void defaultSize() { + assertEquals(100, SpriteSize.DEFAULT.x()); + assertEquals(100, SpriteSize.DEFAULT.y()); + assertEquals("100x100", SpriteSize.DEFAULT.toString()); + } + + @Test + void equalsAndHash() { + SpriteSize size = new SpriteSize(150, 120); + + assertEquals(size, size); + assertEquals(size, new SpriteSize(150, 120)); + assertNotEquals(size, new SpriteSize(150, 130)); + assertNotEquals(size, new SpriteSize(140, 120)); + assertNotEquals(size, new Object()); + assertFalse(size.equals(null)); + + assertEquals(size.hashCode(), size.hashCode()); + assertEquals(size.hashCode(), new SpriteSize(150, 120).hashCode()); + assertNotEquals(size.hashCode(), new SpriteSize(150, 130).hashCode()); + assertNotEquals(size.hashCode(), new SpriteSize(140, 120).hashCode()); + } +} diff --git a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CellAreaTest.java b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CellAreaTest.java index f8d33f387..09f42abf3 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CellAreaTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CellAreaTest.java @@ -44,7 +44,7 @@ public void setUp() throws Exception { @Test void resolve() { assertEquals( - Collections.singleton(map.get(123)), + Collections.singletonList(map.get(123)), new CellArea().resolve(map.get(123), map.get(456)) ); } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CheckboardAreaTest.java b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CheckboardAreaTest.java index c6e5e9001..286350783 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CheckboardAreaTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CheckboardAreaTest.java @@ -53,7 +53,7 @@ void getters() { @Test void resolveSize0() { assertEquals( - Collections.singleton(map.get(123)), + Collections.singletonList(map.get(123)), new CheckboardArea(new EffectArea(EffectArea.Type.CHECKERBOARD, 0)).resolve(map.get(123), map.get(123)) ); } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircleAreaTest.java b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircleAreaTest.java index 664c44107..b9b3726a7 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircleAreaTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CircleAreaTest.java @@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test; import java.util.Collections; +import java.util.List; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertArrayEquals; @@ -56,7 +57,7 @@ void getters() { @Test void resolveSize0() { assertEquals( - Collections.singleton(map.get(123)), + Collections.singletonList(map.get(123)), new CircleArea(new EffectArea(EffectArea.Type.LINE, 0)).resolve(map.get(123), map.get(123)) ); } @@ -96,7 +97,7 @@ void resolveShouldSortByDistanceFromCenter() { } private void assertCellIds(FightCell center, int size, int[] cellIds) { - Set cells = new CircleArea(new EffectArea(EffectArea.Type.CIRCLE, size)).resolve(center, center); + List cells = new CircleArea(new EffectArea(EffectArea.Type.CIRCLE, size)).resolve(center, center); assertArrayEquals( cellIds, diff --git a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CrossAreaTest.java b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CrossAreaTest.java index c4b8c8baf..3d604ed1f 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CrossAreaTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/CrossAreaTest.java @@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test; import java.util.Collections; +import java.util.List; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertArrayEquals; @@ -56,7 +57,7 @@ void getters() { @Test void resolveSize0() { assertEquals( - Collections.singleton(map.get(123)), + Collections.singletonList(map.get(123)), new CrossArea(new EffectArea(EffectArea.Type.LINE, 0)).resolve(map.get(123), map.get(123)) ); } @@ -104,7 +105,7 @@ void resolveShouldSortByDistanceFromCenter() { } private void assertCellIds(FightCell from, FightCell center, int size, int[] cellIds) { - Set cells = new CrossArea(new EffectArea(EffectArea.Type.CROSS, size)).resolve(center, from); + List cells = new CrossArea(new EffectArea(EffectArea.Type.CROSS, size)).resolve(center, from); assertArrayEquals( cellIds, diff --git a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/LineAreaTest.java b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/LineAreaTest.java index ca06eb656..6dcb8401a 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/LineAreaTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/LineAreaTest.java @@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test; import java.util.Collections; +import java.util.List; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertArrayEquals; @@ -56,7 +57,7 @@ void getters() { @Test void resolveSize0() { assertEquals( - Collections.singleton(map.get(123)), + Collections.singletonList(map.get(123)), new LineArea(new EffectArea(EffectArea.Type.LINE, 0)).resolve(map.get(123), map.get(456)) ); } @@ -110,7 +111,7 @@ void resolveShouldSortByDistanceFromCenter() { } private void assertCellIds(FightCell from, FightCell center, int size, int[] cellIds) { - Set cells = new LineArea(new EffectArea(EffectArea.Type.LINE, size)).resolve(center, from); + List cells = new LineArea(new EffectArea(EffectArea.Type.LINE, size)).resolve(center, from); assertArrayEquals( cellIds, diff --git a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/PerpendicularLineAreaTest.java b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/PerpendicularLineAreaTest.java index 5df1c378e..224fd5570 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/PerpendicularLineAreaTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/PerpendicularLineAreaTest.java @@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test; import java.util.Collections; +import java.util.List; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertArrayEquals; @@ -56,7 +57,7 @@ void getters() { @Test void resolveSize0() { assertEquals( - Collections.singleton(map.get(123)), + Collections.singletonList(map.get(123)), new PerpendicularLineArea(new EffectArea(EffectArea.Type.LINE, 0)).resolve(map.get(123), map.get(123)) ); } @@ -86,7 +87,7 @@ void resolveShouldSortByDistanceFromCenter() { } private void assertCellIds(FightCell from, FightCell center, int size, int[] cellIds) { - Set cells = new PerpendicularLineArea(new EffectArea(EffectArea.Type.PERPENDICULAR_LINE, size)).resolve(center, from); + List cells = new PerpendicularLineArea(new EffectArea(EffectArea.Type.PERPENDICULAR_LINE, size)).resolve(center, from); assertArrayEquals( cellIds, diff --git a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/RingAreaTest.java b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/RingAreaTest.java index 01fdeabfe..a6496d076 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/RingAreaTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/RingAreaTest.java @@ -53,7 +53,7 @@ void getters() { @Test void resolveSize0() { assertEquals( - Collections.singleton(map.get(123)), + Collections.singletonList(map.get(123)), new RingArea(new EffectArea(EffectArea.Type.RING, 0)).resolve(map.get(123), map.get(123)) ); } diff --git a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/UtilTest.java b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/UtilTest.java index 57881b3dc..f79a384df 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/UtilTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/spell/effect/area/UtilTest.java @@ -31,6 +31,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -54,7 +55,7 @@ public void setUp() throws Exception { } @Test - void orderedSet() { + void sortCells() { assertOrder( map.get(124), new int[]{94, 95, 96, 109, 110, 123, 124, 125, 138, 139, 152, 154}, @@ -158,12 +159,14 @@ private Direction fullDirection(CoordinateCell from, CoordinateCell target private void assertOrder(MapCell center, int[] cellIds, int[] orderedCells) { - Set cells = Util.createOrderedSet(center); + List cells = new ArrayList<>(); for (int cellId : cellIds) { cells.add(map.get(cellId)); } + Util.sortCells(center, cells); + assertEquals( new ArrayList<>(cells), Arrays.stream(orderedCells).mapToObj(map::get).collect(Collectors.toList()) diff --git a/src/test/java/fr/quatrevieux/araknemu/game/world/creature/accessory/NullAccessoryTest.java b/src/test/java/fr/quatrevieux/araknemu/game/world/creature/accessory/NullAccessoryTest.java index 7996b3eac..a11ffe384 100644 --- a/src/test/java/fr/quatrevieux/araknemu/game/world/creature/accessory/NullAccessoryTest.java +++ b/src/test/java/fr/quatrevieux/araknemu/game/world/creature/accessory/NullAccessoryTest.java @@ -22,11 +22,13 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; class NullAccessoryTest { @Test void data() { - NullAccessory accessory = new NullAccessory(AccessoryType.HELMET); + NullAccessory accessory = NullAccessory.from(AccessoryType.HELMET); assertEquals(AccessoryType.HELMET, accessory.type()); assertEquals(0, accessory.appearance()); @@ -34,4 +36,11 @@ void data() { assertEquals(0, accessory.frame()); assertEquals("", accessory.toString()); } + + @Test + void from() { + assertSame(NullAccessory.from(AccessoryType.HELMET), NullAccessory.from(AccessoryType.HELMET)); + assertSame(NullAccessory.from(AccessoryType.WEAPON), NullAccessory.from(AccessoryType.WEAPON)); + assertNotSame(NullAccessory.from(AccessoryType.HELMET), NullAccessory.from(AccessoryType.WEAPON)); + } } \ No newline at end of file