diff --git a/src/main/java/net/theevilreaper/xerus/api/kit/BaseKit.java b/src/main/java/net/theevilreaper/xerus/api/kit/BaseKit.java index 3018572..3246509 100644 --- a/src/main/java/net/theevilreaper/xerus/api/kit/BaseKit.java +++ b/src/main/java/net/theevilreaper/xerus/api/kit/BaseKit.java @@ -2,18 +2,17 @@ import net.kyori.adventure.key.Key; import net.theevilreaper.xerus.api.component.ObjectComponent; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * The {@link BaseKit} is an abstract layer implementation of the {@link Kit} interface. * It contains the general structure of a kit and can be used to define custom implementations. * * @author theEvilReaper - * @version 1.0.0 + * @version 1.1.0 * @since 1.2.0 **/ public abstract class BaseKit implements Kit { @@ -24,16 +23,16 @@ public abstract class BaseKit implements Kit { /** * Creates a new instance of the {@link BaseKit}. */ - protected BaseKit(@NotNull Key key) { + protected BaseKit(Key key) { this.key = key; - this.components = new HashMap<>(); + this.components = new ConcurrentHashMap<>(); } /** * {@inheritDoc} */ @Override - public void add(@NotNull Class componentClass, @NotNull T component) { + public void add(Class componentClass, T component) { this.components.computeIfAbsent(componentClass, k -> component); } @@ -41,7 +40,7 @@ public void add(@NotNull Class componentClass, @N * {@inheritDoc} */ @Override - public boolean has(@NotNull Class componentClass) { + public boolean has(Class componentClass) { return this.components.containsKey(componentClass); } @@ -49,7 +48,7 @@ public boolean has(@NotNull Class componentClass) * {@inheritDoc}} */ @Override - public @Nullable T get(@NotNull Class componentClass) { + public @Nullable T get(Class componentClass) { return componentClass.cast(this.components.get(componentClass)); } @@ -57,7 +56,7 @@ public boolean has(@NotNull Class componentClass) * {@inheritDoc} */ @Override - public @Nullable T remove(@NotNull Class componentClass) { + public @Nullable T remove(Class componentClass) { return componentClass.cast(this.components.remove(componentClass)); } @@ -65,7 +64,7 @@ public boolean has(@NotNull Class componentClass) * {@inheritDoc} */ @Override - public @NotNull Key key() { + public Key key() { return this.key; } } diff --git a/src/main/java/net/theevilreaper/xerus/api/kit/DefaultKitService.java b/src/main/java/net/theevilreaper/xerus/api/kit/DefaultKitService.java index 01d5191..35b9a26 100644 --- a/src/main/java/net/theevilreaper/xerus/api/kit/DefaultKitService.java +++ b/src/main/java/net/theevilreaper/xerus/api/kit/DefaultKitService.java @@ -2,16 +2,14 @@ import net.kyori.adventure.key.Key; import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnmodifiableView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; /** * The class represents a default implementation of the {@link KitService} interface. @@ -19,19 +17,19 @@ * If your use case doesn't fit into this please implement your own {@link KitService} implementation. * * @author theEvilReaper - * @version 1.1.0 + * @version 1.2.0 * @since 1.2.0 **/ public final class DefaultKitService implements KitService { private static final Logger KIT_LOGGER = LoggerFactory.getLogger(DefaultKitService.class); - private final List kits; + private final Map kits; /** * Creates a new instance of the {@link DefaultKitService}. */ DefaultKitService() { - this.kits = new ArrayList<>(); + this.kits = new ConcurrentHashMap<>(); } /** @@ -47,34 +45,27 @@ public void clear() { * {@inheritDoc} */ @Override - public void add(@NotNull Kit kit) { - if (kits.contains(kit)) { + public void add(Kit kit) { + if (this.kits.containsKey(kit.key())) { KIT_LOGGER.info("Overwriting existing kit!"); } - this.kits.add(kit); + this.kits.put(kit.key(), kit); } /** * {@inheritDoc} */ @Override - public boolean remove(@NotNull Key identifier) { - return this.kits.removeIf(iKit -> iKit.key().equals(identifier)); + public boolean remove(Key identifier) { + return this.kits.remove(identifier) != null; } /** * {@inheritDoc} */ @Override - public @NotNull Optional<@Nullable Kit> getKit(@NotNull Key name) { - Kit kit = null; - for (int i = 0; i < kits.size() && kit == null; i++) { - if (kits.get(i).key().equals(name)) { - kit = kits.get(i); - } - } - - return Optional.ofNullable(kit); + public Optional getKit(Key name) { + return Optional.ofNullable(this.kits.get(name)); } /** @@ -82,7 +73,7 @@ public boolean remove(@NotNull Key identifier) { */ @Contract(pure = true) @Override - public @NotNull @UnmodifiableView List getKits() { - return Collections.unmodifiableList(this.kits); + public @UnmodifiableView List getKits() { + return List.copyOf(this.kits.values()); } } diff --git a/src/main/java/net/theevilreaper/xerus/api/kit/Kit.java b/src/main/java/net/theevilreaper/xerus/api/kit/Kit.java index 578e9af..de97399 100644 --- a/src/main/java/net/theevilreaper/xerus/api/kit/Kit.java +++ b/src/main/java/net/theevilreaper/xerus/api/kit/Kit.java @@ -3,13 +3,12 @@ import net.kyori.adventure.key.Key; import net.minestom.server.entity.Player; import net.theevilreaper.xerus.api.component.Componentable; -import org.jetbrains.annotations.NotNull; /** * The {@link Kit} interface provides all basic methods that a kit should have. * * @author theEvilReaper - * @version 2.0.0 + * @version 2.1.0 * @since 1.2.0 **/ public interface Kit extends Componentable { @@ -19,12 +18,12 @@ public interface Kit extends Componentable { * * @param player the player to apply the kit to */ - void apply(@NotNull Player player); + void apply(Player player); /** * Returns the identifier of the kit. * * @return the underlying value */ - @NotNull Key key(); + Key key(); } diff --git a/src/main/java/net/theevilreaper/xerus/api/kit/KitService.java b/src/main/java/net/theevilreaper/xerus/api/kit/KitService.java index da9a0b5..243522a 100644 --- a/src/main/java/net/theevilreaper/xerus/api/kit/KitService.java +++ b/src/main/java/net/theevilreaper/xerus/api/kit/KitService.java @@ -2,8 +2,6 @@ import net.kyori.adventure.key.Key; import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnmodifiableView; import java.util.List; @@ -15,7 +13,7 @@ * So he must implement the interface by self and can add additional method to the service. * * @author theEvilReaper - * @version 1.0.0 + * @version 1.1.0 * @since 1.2.0 **/ public interface KitService { @@ -26,7 +24,7 @@ public interface KitService { * @return the new instance */ @Contract(pure = true) - static @NotNull KitService of() { + static KitService of() { return new DefaultKitService(); } @@ -35,7 +33,7 @@ public interface KitService { * * @param kit that should be added */ - void add(@NotNull Kit kit); + void add(Kit kit); /** * Remove a kit by the identifier from the kit. @@ -43,7 +41,7 @@ public interface KitService { * @param identifier the identifier from the kit * @return true when the kit can be removed otherwise false */ - boolean remove(@NotNull Key identifier); + boolean remove(Key identifier); /** * Clears the underlying data structure. @@ -56,14 +54,13 @@ public interface KitService { * @param name the name of the kit * @return the fetched kit in an optional */ - @NotNull Optional<@Nullable Kit> getKit(@NotNull Key name); + Optional getKit(Key name); /** * Returns a list with all current available kits. * * @return the underlying list */ - @NotNull @UnmodifiableView @Contract(pure = true) List getKits(); diff --git a/src/main/java/net/theevilreaper/xerus/api/kit/event/PlayerKitChangeEvent.java b/src/main/java/net/theevilreaper/xerus/api/kit/event/PlayerKitChangeEvent.java index 37582db..ae00845 100644 --- a/src/main/java/net/theevilreaper/xerus/api/kit/event/PlayerKitChangeEvent.java +++ b/src/main/java/net/theevilreaper/xerus/api/kit/event/PlayerKitChangeEvent.java @@ -4,7 +4,6 @@ import net.minestom.server.entity.Player; import net.minestom.server.event.trait.CancellableEvent; import net.minestom.server.event.trait.PlayerEvent; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -16,7 +15,7 @@ public final class PlayerKitChangeEvent implements PlayerEvent, CancellableEvent { private final Player player; - private final Kit currentKit; + private final @Nullable Kit currentKit; private final Kit newKit; private boolean cancelled; @@ -26,7 +25,7 @@ public final class PlayerKitChangeEvent implements PlayerEvent, CancellableEvent * @param currentKit The current kit of the player * @param newKit The new kit of the player */ - public PlayerKitChangeEvent(@NotNull Player player, @Nullable Kit currentKit, @NotNull Kit newKit) { + public PlayerKitChangeEvent(Player player, @Nullable Kit currentKit, Kit newKit) { this.player = player; this.currentKit = currentKit; this.newKit = newKit; @@ -37,7 +36,7 @@ public PlayerKitChangeEvent(@NotNull Player player, @Nullable Kit currentKit, @N * @return the involved player */ @Override - public @NotNull Player getPlayer() { + public Player getPlayer() { return player; } @@ -75,7 +74,7 @@ public boolean isCancelled() { * Returns the new kit for a player * @return the new kit */ - public @NotNull Kit getNewKit() { + public Kit getNewKit() { return newKit; } } \ No newline at end of file diff --git a/src/main/java/net/theevilreaper/xerus/api/kit/event/package-info.java b/src/main/java/net/theevilreaper/xerus/api/kit/event/package-info.java new file mode 100644 index 0000000..ea70117 --- /dev/null +++ b/src/main/java/net/theevilreaper/xerus/api/kit/event/package-info.java @@ -0,0 +1,4 @@ +@NotNullByDefault +package net.theevilreaper.xerus.api.kit.event; + +import org.jetbrains.annotations.NotNullByDefault; \ No newline at end of file diff --git a/src/main/java/net/theevilreaper/xerus/api/kit/package-info.java b/src/main/java/net/theevilreaper/xerus/api/kit/package-info.java new file mode 100644 index 0000000..ffb4c87 --- /dev/null +++ b/src/main/java/net/theevilreaper/xerus/api/kit/package-info.java @@ -0,0 +1,4 @@ +@NotNullByDefault +package net.theevilreaper.xerus.api.kit; + +import org.jetbrains.annotations.NotNullByDefault; \ No newline at end of file diff --git a/src/test/java/net/theevilreaper/xerus/api/kit/KitServiceTest.java b/src/test/java/net/theevilreaper/xerus/api/kit/KitServiceTest.java index 80aec50..df2feb3 100644 --- a/src/test/java/net/theevilreaper/xerus/api/kit/KitServiceTest.java +++ b/src/test/java/net/theevilreaper/xerus/api/kit/KitServiceTest.java @@ -62,4 +62,42 @@ void testKitGetNonExisting() { Optional fetchedKit = kitService.getKit(Key.key("xerus", "test_kit")); assertFalse(fetchedKit.isPresent()); } + + @Test + void testDuplicateKitAddition() { + assertTrue(kitService.getKits().isEmpty()); + + Kit kit1 = new TestKit(); + Kit kit2 = new TestKit(); + + kitService.add(kit1); + assertEquals(1, kitService.getKits().size()); + + // Adding kit with same key must replace existing without duplicating size + kitService.add(kit2); + assertEquals(1, kitService.getKits().size()); + assertEquals(kit2, kitService.getKit(kit1.key()).orElse(null)); + } + + @Test + void testRemoveNonExisting() { + assertFalse(kitService.remove(Key.key("xerus", "non_existing_kit"))); + } + + @Test + void testClearMultipleKits() { + kitService.add(new TestKit(Key.key("xerus", "kit_1"))); + kitService.add(new TestKit(Key.key("xerus", "kit_2"))); + assertEquals(2, kitService.getKits().size()); + + kitService.clear(); + assertTrue(kitService.getKits().isEmpty()); + } + + @Test + void testUnmodifiableKitsList() { + kitService.add(new TestKit()); + var kitsList = kitService.getKits(); + assertThrows(UnsupportedOperationException.class, () -> kitsList.add(new TestKit(Key.key("xerus", "other")))); + } } diff --git a/src/test/java/net/theevilreaper/xerus/api/kit/PlayerKitIntegrationTest.java b/src/test/java/net/theevilreaper/xerus/api/kit/PlayerKitIntegrationTest.java new file mode 100644 index 0000000..c22415a --- /dev/null +++ b/src/test/java/net/theevilreaper/xerus/api/kit/PlayerKitIntegrationTest.java @@ -0,0 +1,44 @@ +package net.theevilreaper.xerus.api.kit; + +import net.kyori.adventure.key.Key; +import net.minestom.server.entity.Player; +import net.minestom.server.event.EventDispatcher; +import net.minestom.server.event.EventFilter; + +import net.minestom.server.instance.Instance; +import net.minestom.testing.Collector; +import net.minestom.testing.Env; +import net.minestom.testing.extension.MicrotusExtension; +import net.theevilreaper.xerus.api.kit.event.PlayerKitChangeEvent; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +@ExtendWith(MicrotusExtension.class) +class PlayerKitIntegrationTest { + + @Test + void testPlayerKitChangeEventFiring(Env env) { + Instance instance = env.createFlatInstance(); + Player player = env.createPlayer(instance); + + Kit oldKit = new TestKit(Key.key("xerus", "old_kit")); + Kit newKit = new TestKit(Key.key("xerus", "new_kit")); + + Collector collector = env.trackEvent(PlayerKitChangeEvent.class, EventFilter.PLAYER, player); + + PlayerKitChangeEvent event = new PlayerKitChangeEvent(player, oldKit, newKit); + EventDispatcher.call(event); + + collector.assertSingle(firedEvent -> { + assertEquals(player, firedEvent.getPlayer()); + assertEquals(oldKit, firedEvent.getCurrentKit()); + assertEquals(newKit, firedEvent.getNewKit()); + assertFalse(firedEvent.isCancelled()); + }); + + env.destroyInstance(instance, true); + } +}