diff --git a/src/main/java/net/theevilreaper/xerus/api/team/DefaultTeam.java b/src/main/java/net/theevilreaper/xerus/api/team/DefaultTeam.java index 0296077..6e55e41 100644 --- a/src/main/java/net/theevilreaper/xerus/api/team/DefaultTeam.java +++ b/src/main/java/net/theevilreaper/xerus/api/team/DefaultTeam.java @@ -4,14 +4,12 @@ import net.minestom.server.entity.Player; import net.minestom.server.utils.validate.Check; import net.theevilreaper.xerus.api.component.ObjectComponent; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * The class represents a default implementation of the {@link Team} interface. @@ -19,7 +17,7 @@ * If a case doesn't fit into this, it is recommended to create a custom implementation or inherit from this class. * * @author theEvilReaper - * @version 2.0.0 + * @version 2.1.0 * @since 1.0.0 */ public class DefaultTeam implements Team { @@ -39,22 +37,18 @@ public class DefaultTeam implements Team { * @param key the key for the team name * @param initialCapacity the initial capacity for the team */ - protected DefaultTeam(@NotNull Key key, int initialCapacity) { + protected DefaultTeam(Key key, int initialCapacity) { this.key = key; this.capacity = initialCapacity; - if (initialCapacity == DEFAULT_CAPACITY) { - this.players = new HashSet<>(); - } else { - this.players = HashSet.newHashSet(initialCapacity); - } - this.components = new HashMap<>(); + this.players = ConcurrentHashMap.newKeySet(); + 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); } @@ -62,7 +56,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); } @@ -70,7 +64,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)); } @@ -78,7 +72,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)); } @@ -87,7 +81,7 @@ public boolean has(@NotNull Class componentClass) */ @Override public void setCapacity(int capacity) { - Check.argCondition(capacity < 0, "The capacity of the can't be negative"); + Check.argCondition(capacity < 0 && capacity != DEFAULT_CAPACITY, "The capacity of the team can't be negative"); this.capacity = capacity; } @@ -100,7 +94,7 @@ public boolean canJoin() { } @Override - public int compare(@NotNull Team o1, @NotNull Team o2) { + public int compare(Team o1, Team o2) { return o1.key().compareTo(o2.key()); } @@ -119,7 +113,7 @@ public int hashCode() { * {@inheritDoc} */ @Override - public @NotNull Key key() { + public Key key() { return this.key; } @@ -143,7 +137,7 @@ public int getCurrentSize() { * {@inheritDoc} */ @Override - public @NotNull Set getPlayers() { + public Set getPlayers() { return players; } } \ No newline at end of file diff --git a/src/main/java/net/theevilreaper/xerus/api/team/StandardTeamService.java b/src/main/java/net/theevilreaper/xerus/api/team/StandardTeamService.java index 177e539..7b62142 100644 --- a/src/main/java/net/theevilreaper/xerus/api/team/StandardTeamService.java +++ b/src/main/java/net/theevilreaper/xerus/api/team/StandardTeamService.java @@ -2,53 +2,56 @@ import net.kyori.adventure.key.Key; import net.minestom.server.entity.Player; -import org.jetbrains.annotations.*; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; -import java.util.ArrayList; -import java.util.Collections; +import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; /** * The default implementation of the {@link TeamService} interface. * * @author theEvilReaper - * @version 1.1.0 + * @version 1.3.0 * @since 1.0.1 */ public final class StandardTeamService implements TeamService { - private final List teams; + private final Map teams; /** * Creates a new instance from the {@link StandardTeamService}. */ StandardTeamService() { - this.teams = new ArrayList<>(); + this.teams = new ConcurrentHashMap<>(); } /** * {@inheritDoc} */ @Override - public void add(@NotNull Team team) { - this.teams.add(team); + public void add(Team team) { + this.teams.put(team.key(), team); } /** * {@inheritDoc} */ @Override - public void remove(@NotNull Team team) { - this.teams.remove(team); + public void remove(Team team) { + this.teams.remove(team.key()); } /** * {@inheritDoc} */ @Override - public void remove(@NotNull Key identifier) { - this.teams.removeIf(team -> team.key().equals(identifier)); + public void remove(Key identifier) { + this.teams.remove(identifier); } /** @@ -58,8 +61,8 @@ public void remove(@NotNull Key identifier) { public void clear() { if (this.teams.isEmpty()) return; - for (int i = 0; i < this.teams.size(); i++) { - this.teams.get(i).clearPlayers(); + for (Team team : this.teams.values()) { + team.clearPlayers(); } this.teams.clear(); @@ -69,48 +72,30 @@ public void clear() { * {@inheritDoc} */ @Override - public @NotNull Optional<@Nullable Team> getTeam(@NotNull Key identifier) { - int i = 0; - - while (i < teams.size() && !teams.get(i).key().equals(identifier)) { - i++; - } - - return Optional.ofNullable(teams.get(i)); + public Optional<@Nullable Team> getTeam(Key identifier) { + return Optional.ofNullable(this.teams.get(identifier)); } /** * {@inheritDoc} */ @Override - public boolean exists(@NotNull Key identifier) { - if (teams.isEmpty()) { - return false; - } - - int i = 0; - - while (i < teams.size() && !teams.get(i).key().equals(identifier)) { - i++; - } - - return i != teams.size(); + public boolean exists(Key identifier) { + return this.teams.containsKey(identifier); } /** * {@inheritDoc} */ @Override - public @NotNull Optional<@Nullable Team> getTeam(@NotNull Player player) { - Team team = null; - - for (int i = 0; i < getTeams().size() && team == null; i++) { - if (getTeams().get(i).hasPlayer(player)) { - team = getTeams().get(i); + public Optional<@Nullable Team> getTeam(Player player) { + for (Team team : this.teams.values()) { + if (team.hasPlayer(player)) { + return Optional.of(team); } } - return Optional.ofNullable(team); + return Optional.empty(); } /** @@ -118,19 +103,7 @@ public boolean exists(@NotNull Key identifier) { */ @Override public Optional<@Nullable Team> getSmallestTeam() { - if (!teams.isEmpty()) { - int i = 1; - var team = teams.getFirst(); - - while (i < teams.size() && teams.get(i).getCurrentSize() < team.getCurrentSize()) { - team = teams.get(i); - i++; - } - - return Optional.ofNullable(team); - } - - return Optional.empty(); + return this.teams.values().stream().min(Comparator.comparingInt(Team::getCurrentSize)); } /** @@ -138,7 +111,7 @@ public boolean exists(@NotNull Key identifier) { */ @Contract(pure = true) @Override - public @NotNull @UnmodifiableView List getTeams() { - return Collections.unmodifiableList(teams); + public @UnmodifiableView List getTeams() { + return List.copyOf(this.teams.values()); } } \ No newline at end of file diff --git a/src/main/java/net/theevilreaper/xerus/api/team/Team.java b/src/main/java/net/theevilreaper/xerus/api/team/Team.java index b687cc1..dc455ca 100644 --- a/src/main/java/net/theevilreaper/xerus/api/team/Team.java +++ b/src/main/java/net/theevilreaper/xerus/api/team/Team.java @@ -13,6 +13,7 @@ import java.util.Collection; import java.util.Comparator; +import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; @@ -21,17 +22,11 @@ * It defines essential methods for organizing and interacting with a team of players or members. * * @author theEvilReaper - * @version 2.0.0 + * @version 2.2.0 * @since 1.1.6 **/ public interface Team extends Joinable, Componentable, Comparator { - /** - * An empty runnable implementation. - */ - @NotNull Runnable EMPTY = () -> { - }; - /** * Creates a new {@link Team} instance with the given key. * @@ -39,7 +34,7 @@ public interface Team extends Joinable, Componentable, Comparator { * @return the created team */ @Contract("_ -> new") - static @NotNull Team of(@NotNull Key key) { + static Team of(Key key) { return new DefaultTeam(key, -1); } @@ -51,7 +46,7 @@ public interface Team extends Joinable, Componentable, Comparator { * @return the created team */ @Contract("_, _ -> new") - static @NotNull Team of(@NotNull Key key, int capacity) { + static Team of(Key key, int capacity) { return new DefaultTeam(key, capacity); } @@ -63,10 +58,13 @@ public interface Team extends Joinable, Componentable, Comparator { */ @Override default void addPlayer(@NotNull Player player, @Nullable Consumer consumer) { - if (getPlayers().add(player)) { - PlayerTeamEvent teamEvent = new PlayerTeamEvent(player, this, TeamAction.ADD); - EventDispatcher.callCancellable(teamEvent, consumer != null ? () -> consumer.accept(player) : EMPTY); - } + if (!canJoin()) return; + PlayerTeamEvent teamEvent = new PlayerTeamEvent(player, this, TeamAction.ADD); + EventDispatcher.callCancellable(teamEvent, () -> { + if (getPlayers().add(player) && consumer != null) { + consumer.accept(player); + } + }); } /** @@ -76,11 +74,18 @@ default void addPlayer(@NotNull Player player, @Nullable Consumer consum * @param consumer a consumer which is called to execute some logic */ @Override - default void addPlayers(@NotNull Collection players, @Nullable Consumer consumer) { + default void addPlayers(Collection players, @Nullable Consumer consumer) { if (players.isEmpty()) return; - players.removeIf(player -> !getPlayers().add(player)); - Runnable successCallback = consumer == null ? EMPTY : () -> getPlayers().forEach(consumer); - EventDispatcher.callCancellable(new MultiPlayerTeamEvent(this, players, TeamAction.ADD), successCallback); + Set toAdd = new HashSet<>(players); + toAdd.removeIf(this::hasPlayer); + if (toAdd.isEmpty()) return; + EventDispatcher.callCancellable(new MultiPlayerTeamEvent(this, toAdd, TeamAction.ADD), () -> { + for (Player player : toAdd) { + if (canJoin() && getPlayers().add(player) && consumer != null) { + consumer.accept(player); + } + } + }); } /** @@ -90,11 +95,13 @@ default void addPlayers(@NotNull Collection players, @Nullable Consumer< * @param consumer a consumer which is called to execute some logic */ @Override - default void removePlayer(@NotNull Player paramPlayer, @Nullable Consumer consumer) { - if (getPlayers().remove(paramPlayer)) { - PlayerTeamEvent teamEvent = new PlayerTeamEvent(paramPlayer, this, TeamAction.REMOVE); - EventDispatcher.callCancellable(teamEvent, consumer == null ? EMPTY : () -> consumer.accept(paramPlayer)); - } + default void removePlayer(Player paramPlayer, @Nullable Consumer consumer) { + PlayerTeamEvent teamEvent = new PlayerTeamEvent(paramPlayer, this, TeamAction.REMOVE); + EventDispatcher.callCancellable(teamEvent, () -> { + if (getPlayers().remove(paramPlayer) && consumer != null) { + consumer.accept(paramPlayer); + } + }); } /** @@ -104,11 +111,19 @@ default void removePlayer(@NotNull Player paramPlayer, @Nullable Consumer players, @Nullable Consumer consumer) { + default void removePlayers(Collection players, @Nullable Consumer consumer) { if (players.isEmpty()) return; - players.removeIf(player -> getPlayers().contains(player)); - Runnable successCallback = consumer == null ? EMPTY : () -> getPlayers().forEach(consumer); - EventDispatcher.callCancellable(new MultiPlayerTeamEvent(this, players, TeamAction.REMOVE), successCallback); + Set toRemove = new HashSet<>(players); + toRemove.retainAll(getPlayers()); + if (toRemove.isEmpty()) return; + EventDispatcher.callCancellable(new MultiPlayerTeamEvent(this, toRemove, TeamAction.REMOVE), () -> { + for (Player player : toRemove) { + getPlayers().remove(player); + if (consumer != null) { + consumer.accept(player); + } + } + }); } /** @@ -131,7 +146,7 @@ default void removePlayers(@NotNull Collection players, @Nullable Consum * @param player the player to check * @return true when the player is in the team otherwise false */ - default boolean hasPlayer(@NotNull Player player) { + default boolean hasPlayer(Player player) { return getPlayers().contains(player); } @@ -140,7 +155,7 @@ default boolean hasPlayer(@NotNull Player player) { * * @param component the message wraps as {@link Component} who should send */ - default void sendMessage(@NotNull Component component) { + default void sendMessage(Component component) { if (isEmpty()) return; for (Player player : getPlayers()) { player.sendMessage(component); @@ -152,7 +167,7 @@ default void sendMessage(@NotNull Component component) { */ default void clearPlayers() { if (getPlayers().isEmpty()) return; - removePlayers(getPlayers()); + removePlayers(new HashSet<>(getPlayers()), null); } /** @@ -160,7 +175,7 @@ default void clearPlayers() { * * @return the identifier */ - @NotNull Key key(); + Key key(); /** * Returns a boolean indicator if the team contains entries or not. @@ -190,5 +205,5 @@ default boolean isEmpty() { * * @return the given set */ - @NotNull Set getPlayers(); + Set getPlayers(); } diff --git a/src/main/java/net/theevilreaper/xerus/api/team/TeamService.java b/src/main/java/net/theevilreaper/xerus/api/team/TeamService.java index 932cbda..81f9044 100644 --- a/src/main/java/net/theevilreaper/xerus/api/team/TeamService.java +++ b/src/main/java/net/theevilreaper/xerus/api/team/TeamService.java @@ -4,7 +4,6 @@ import net.minestom.server.entity.Player; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -15,7 +14,7 @@ * It contains management methods like add, remove or other methods to retrieve teams. * * @author theEvilReaper - * @version 2.0.0 + * @version 2.1.0 * @since 1.1.0 **/ @ApiStatus.NonExtendable @@ -27,7 +26,7 @@ public interface TeamService { * @return the new instance */ @Contract(pure = true) - static @NotNull TeamService of() { + static TeamService of() { return new StandardTeamService(); } @@ -36,14 +35,14 @@ public interface TeamService { * * @param team which should be added */ - void add(@NotNull Team team); + void add(Team team); /** * Remove a team from the service. * * @param team which should be removed */ - void remove(@NotNull Team team); + void remove(Team team); /** * Removes the team by his given identifier. @@ -51,7 +50,7 @@ public interface TeamService { * * @param identifier the identifier from the team */ - void remove(@NotNull Key identifier); + void remove(Key identifier); /** * Clears the underlying team list. @@ -65,7 +64,7 @@ public interface TeamService { * @param identifier the identifier of the team * @return true if the team exists otherwise false */ - boolean exists(@NotNull Key identifier); + boolean exists(Key identifier); /** * Returns the team based on the specified identifier. @@ -73,7 +72,7 @@ public interface TeamService { * @param identifier of the team * @return the team in an {@link Optional} */ - Optional<@Nullable Team> getTeam(@NotNull Key identifier); + Optional<@Nullable Team> getTeam(Key identifier); /** * Returns the team based on the given player. @@ -81,7 +80,7 @@ public interface TeamService { * @param player The player from which the team is determined * @return the team in an {@link Optional} */ - Optional<@Nullable Team> getTeam(@NotNull Player player); + Optional<@Nullable Team> getTeam(Player player); /** * Returns the team with the fewest players. @@ -104,5 +103,5 @@ default boolean hasTeams() { * * @return the underlying list */ - @NotNull List getTeams(); + List getTeams(); } \ No newline at end of file diff --git a/src/main/java/net/theevilreaper/xerus/api/team/distribution/DefaultTeamDistributor.java b/src/main/java/net/theevilreaper/xerus/api/team/distribution/DefaultTeamDistributor.java index a069f63..92a1fb5 100644 --- a/src/main/java/net/theevilreaper/xerus/api/team/distribution/DefaultTeamDistributor.java +++ b/src/main/java/net/theevilreaper/xerus/api/team/distribution/DefaultTeamDistributor.java @@ -13,7 +13,7 @@ * Default implementation of the {@link TeamDistributor} interface. * * @author Patrick Zdarsky / Rxcki - * @version 1.0 + * @version 1.1 * @since 03/02/2020 20:32 */ public class DefaultTeamDistributor implements TeamDistributor { @@ -23,8 +23,8 @@ public class DefaultTeamDistributor implements TeamDistributor { */ @SuppressWarnings("java:S3776") @Override - public void distribute(@NotNull List teams, @NotNull List players, int teamSize, - @NotNull ToIntFunction eloFunction, boolean evenTeams, boolean lowVariance) { + public void distribute(List teams, List players, int teamSize, + ToIntFunction eloFunction, boolean evenTeams, boolean lowVariance) { if (teams.isEmpty()) { throw new IllegalArgumentException("The list with the teams can not be empty"); } @@ -63,7 +63,7 @@ public void distribute(@NotNull List teams, @NotNull List players, team = teams.get(i); } - if (team == null) return; + if (team == null) continue; for (DistributionPlayer player : distributionTeam.players()) { final Player realPlayer = MinecraftServer.getConnectionManager().getOnlinePlayerByUuid(player.uuid()); @@ -78,8 +78,8 @@ public void distribute(@NotNull List teams, @NotNull List players, * {@inheritDoc} */ @Override - public void distribute(@NotNull List teams, @NotNull List players, int teamSize, - @NotNull ToIntFunction eloFunction) { + public void distribute(List teams, List players, int teamSize, + ToIntFunction eloFunction) { distribute(teams, players, teamSize, eloFunction, true, false); } } diff --git a/src/main/java/net/theevilreaper/xerus/api/team/distribution/DistributionPlayer.java b/src/main/java/net/theevilreaper/xerus/api/team/distribution/DistributionPlayer.java index 8269b19..d54d883 100644 --- a/src/main/java/net/theevilreaper/xerus/api/team/distribution/DistributionPlayer.java +++ b/src/main/java/net/theevilreaper/xerus/api/team/distribution/DistributionPlayer.java @@ -1,7 +1,5 @@ package net.theevilreaper.xerus.api.team.distribution; -import org.jetbrains.annotations.NotNull; - import java.util.UUID; /** @@ -16,5 +14,5 @@ * @version 1.1.0 * @since 03/02/2020 20:26 */ -public record DistributionPlayer(@NotNull UUID uuid, int elo) { +public record DistributionPlayer(UUID uuid, int elo) { } diff --git a/src/main/java/net/theevilreaper/xerus/api/team/distribution/DistributionTeam.java b/src/main/java/net/theevilreaper/xerus/api/team/distribution/DistributionTeam.java index 0bdb05b..0d1dbc6 100644 --- a/src/main/java/net/theevilreaper/xerus/api/team/distribution/DistributionTeam.java +++ b/src/main/java/net/theevilreaper/xerus/api/team/distribution/DistributionTeam.java @@ -1,7 +1,6 @@ package net.theevilreaper.xerus.api.team.distribution; import net.kyori.adventure.key.Key; -import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; @@ -16,14 +15,14 @@ * @version 1.0 * @since 03/02/2020 20:28 */ -public record DistributionTeam(@NotNull Key name, List players) { +public record DistributionTeam(Key name, List players) { /** * Creates a new instance of the {@link DistributionTeam} with the given value. * * @param name the name of the team */ - public DistributionTeam(@NotNull Key name) { + public DistributionTeam(Key name) { this(name, new ArrayList<>()); } @@ -65,7 +64,7 @@ public int length() { * * @param players the players to add */ - public void addAll(@NotNull List players) { + public void addAll(List players) { this.players.addAll(players); } @@ -74,7 +73,7 @@ public void addAll(@NotNull List players) { * * @param player the player to add */ - public void add(@NotNull DistributionPlayer player) { + public void add(DistributionPlayer player) { players.add(player); } } diff --git a/src/main/java/net/theevilreaper/xerus/api/team/distribution/Splitter.java b/src/main/java/net/theevilreaper/xerus/api/team/distribution/Splitter.java index f976290..241983c 100644 --- a/src/main/java/net/theevilreaper/xerus/api/team/distribution/Splitter.java +++ b/src/main/java/net/theevilreaper/xerus/api/team/distribution/Splitter.java @@ -11,7 +11,7 @@ * Taken from: ... * * @author Patrick Zdarsky / Rxcki - * @version 1.0 + * @version 1.1 * @since 03/02/2020 20:21 */ public class Splitter { @@ -155,9 +155,11 @@ private void greedy(DistributionTeam[] cts, DistributionPlayer @NotNull [] ps, @ */ private DistributionTeam minTeam(DistributionTeam[] ts) { int i = 0; - if (evenTeams) - while (ts[i].length() >= evenMax) + if (evenTeams) { + while (i < tn - 1 && ts[i].length() >= evenMax) { i++; + } + } DistributionTeam mt = ts[i]; int min = mt.sum(); for (int j = 1; j < tn; j++) { diff --git a/src/main/java/net/theevilreaper/xerus/api/team/distribution/TeamDistributor.java b/src/main/java/net/theevilreaper/xerus/api/team/distribution/TeamDistributor.java index e3fe37e..045924f 100644 --- a/src/main/java/net/theevilreaper/xerus/api/team/distribution/TeamDistributor.java +++ b/src/main/java/net/theevilreaper/xerus/api/team/distribution/TeamDistributor.java @@ -2,7 +2,6 @@ import net.theevilreaper.xerus.api.team.Team; import net.minestom.server.entity.Player; -import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.function.ToIntFunction; @@ -28,10 +27,10 @@ public interface TeamDistributor { * @param lowVariance if true, the distribution will aim for low variance in team elo ratings */ void distribute( - @NotNull List teams, - @NotNull List players, + List teams, + List players, int teamSize, - @NotNull ToIntFunction eloFunction, + ToIntFunction eloFunction, boolean evenTeams, boolean lowVariance ); @@ -45,9 +44,9 @@ void distribute( * @param eloFunction a function to get the elo rating of a player */ void distribute( - @NotNull List teams, - @NotNull List players, + List teams, + List players, int teamSize, - @NotNull ToIntFunction eloFunction + ToIntFunction eloFunction ); } diff --git a/src/main/java/net/theevilreaper/xerus/api/team/distribution/package-info.java b/src/main/java/net/theevilreaper/xerus/api/team/distribution/package-info.java new file mode 100644 index 0000000..98de9c7 --- /dev/null +++ b/src/main/java/net/theevilreaper/xerus/api/team/distribution/package-info.java @@ -0,0 +1,4 @@ +@NotNullByDefault +package net.theevilreaper.xerus.api.team.distribution; + +import org.jetbrains.annotations.NotNullByDefault; \ No newline at end of file diff --git a/src/main/java/net/theevilreaper/xerus/api/team/event/MultiPlayerTeamEvent.java b/src/main/java/net/theevilreaper/xerus/api/team/event/MultiPlayerTeamEvent.java index 0bf9e4f..cfe07d4 100644 --- a/src/main/java/net/theevilreaper/xerus/api/team/event/MultiPlayerTeamEvent.java +++ b/src/main/java/net/theevilreaper/xerus/api/team/event/MultiPlayerTeamEvent.java @@ -4,7 +4,6 @@ import net.minestom.server.entity.Player; import net.minestom.server.event.Event; import net.minestom.server.event.trait.CancellableEvent; -import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -30,7 +29,7 @@ public class MultiPlayerTeamEvent implements Event, CancellableEvent { * @param players the collection with the players who are involved in the event * @param teamAction the action for the event */ - public MultiPlayerTeamEvent(@NotNull Team team, @NotNull Collection players, @NotNull TeamAction teamAction) { + public MultiPlayerTeamEvent(Team team, Collection players, TeamAction teamAction) { this.team = team; this.players = players; this.action = teamAction; @@ -61,7 +60,6 @@ public boolean isCancelled() { * * @return the collection with the involved players */ - @NotNull public Collection getPlayers() { return players; } @@ -71,7 +69,7 @@ public Collection getPlayers() { * * @return the involved team */ - public @NotNull Team getTeam() { + public Team getTeam() { return team; } @@ -80,7 +78,7 @@ public Collection getPlayers() { * * @return the performed action */ - public @NotNull TeamAction getAction() { + public TeamAction getAction() { return action; } } diff --git a/src/main/java/net/theevilreaper/xerus/api/team/event/PlayerTeamEvent.java b/src/main/java/net/theevilreaper/xerus/api/team/event/PlayerTeamEvent.java index 20b0005..a9b4136 100644 --- a/src/main/java/net/theevilreaper/xerus/api/team/event/PlayerTeamEvent.java +++ b/src/main/java/net/theevilreaper/xerus/api/team/event/PlayerTeamEvent.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; /** * The {@link PlayerTeamEvent} is called when a player changes his given team reference. @@ -28,7 +27,7 @@ public final class PlayerTeamEvent implements PlayerEvent, CancellableEvent { * @param team the current team from the player * @param action the action which was applied to the team */ - public PlayerTeamEvent(@NotNull Player player, @NotNull Team team, @NotNull TeamAction action) { + public PlayerTeamEvent(Player player, Team team, TeamAction action) { this.player = player; this.team = team; this.action = action; @@ -59,7 +58,7 @@ public boolean isCancelled() { * * @return the involved team */ - public @NotNull Team getTeam() { + public Team getTeam() { return team; } @@ -68,7 +67,7 @@ public boolean isCancelled() { * * @return the performed action */ - public @NotNull TeamAction getAction() { + public TeamAction getAction() { return action; } @@ -78,7 +77,7 @@ public boolean isCancelled() { * @return the involved player */ @Override - public @NotNull Player getPlayer() { + public Player getPlayer() { return player; } } diff --git a/src/main/java/net/theevilreaper/xerus/api/team/event/package-info.java b/src/main/java/net/theevilreaper/xerus/api/team/event/package-info.java new file mode 100644 index 0000000..2fce9bb --- /dev/null +++ b/src/main/java/net/theevilreaper/xerus/api/team/event/package-info.java @@ -0,0 +1,4 @@ +@NotNullByDefault +package net.theevilreaper.xerus.api.team.event; + +import org.jetbrains.annotations.NotNullByDefault; \ No newline at end of file diff --git a/src/main/java/net/theevilreaper/xerus/api/team/package-info.java b/src/main/java/net/theevilreaper/xerus/api/team/package-info.java new file mode 100644 index 0000000..1ebdace --- /dev/null +++ b/src/main/java/net/theevilreaper/xerus/api/team/package-info.java @@ -0,0 +1,4 @@ +@NotNullByDefault +package net.theevilreaper.xerus.api.team; + +import org.jetbrains.annotations.NotNullByDefault; \ No newline at end of file diff --git a/src/test/java/net/theevilreaper/xerus/api/team/TeamServiceIntegrationTest.java b/src/test/java/net/theevilreaper/xerus/api/team/TeamServiceIntegrationTest.java index 564f30e..3ce5cd1 100644 --- a/src/test/java/net/theevilreaper/xerus/api/team/TeamServiceIntegrationTest.java +++ b/src/test/java/net/theevilreaper/xerus/api/team/TeamServiceIntegrationTest.java @@ -75,4 +75,27 @@ void testTeamExists() { assertTrue(teamService.exists(Key.key("xerus", "team_yellow"))); assertFalse(teamService.exists(Key.key("xerus", "team_green"))); } + + @Test + void testGetNonExistingTeam() { + assertFalse(teamService.hasTeams()); + Optional team = teamService.getTeam(Key.key("xerus", "non_existing")); + assertTrue(team.isEmpty()); + } + + @Test + void testGetSmallestTeam() { + assertFalse(teamService.hasTeams()); + assertTrue(teamService.getSmallestTeam().isEmpty()); + + Team team1 = Team.of(Key.key("xerus", "team_1")); + Team team2 = Team.of(Key.key("xerus", "team_2")); + teamService.add(team1); + teamService.add(team2); + + // team1 has 0 players, team2 has 0 players - smallest is either (size 0) + assertTrue(teamService.getSmallestTeam().isPresent()); + assertEquals(0, teamService.getSmallestTeam().get().getCurrentSize()); + } } + diff --git a/src/test/java/net/theevilreaper/xerus/api/team/TeamTest.java b/src/test/java/net/theevilreaper/xerus/api/team/TeamTest.java index da92f50..1d023be 100644 --- a/src/test/java/net/theevilreaper/xerus/api/team/TeamTest.java +++ b/src/test/java/net/theevilreaper/xerus/api/team/TeamTest.java @@ -16,8 +16,8 @@ void testInvalidCapacityUpdate() { assertNotNull(team); assertThrowsExactly( IllegalArgumentException.class, - () -> team.setCapacity(-1), - "The capacity of the can't be negative" + () -> team.setCapacity(-5), + "The capacity of the team can't be negative" ); } @@ -30,6 +30,9 @@ void testCapacityUpdate() { team.setCapacity(10); assertEquals(10, team.getCapacity()); + + team.setCapacity(-1); + assertEquals(-1, team.getCapacity()); } @Test diff --git a/src/test/java/net/theevilreaper/xerus/api/team/distribution/DefaultTeamDistributorTest.java b/src/test/java/net/theevilreaper/xerus/api/team/distribution/DefaultTeamDistributorTest.java new file mode 100644 index 0000000..8dcaef2 --- /dev/null +++ b/src/test/java/net/theevilreaper/xerus/api/team/distribution/DefaultTeamDistributorTest.java @@ -0,0 +1,52 @@ +package net.theevilreaper.xerus.api.team.distribution; + +import net.kyori.adventure.key.Key; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class DefaultTeamDistributorTest { + + @Test + void testSplitterLowVarianceFullCapacity() { + Splitter splitter = new Splitter(); + DistributionTeam[] dTeams = new DistributionTeam[]{ + new DistributionTeam(Key.key("xerus", "red")), + new DistributionTeam(Key.key("xerus", "blue")) + }; + DistributionPlayer[] dPlayers = new DistributionPlayer[]{ + new DistributionPlayer(UUID.randomUUID(), 100), + new DistributionPlayer(UUID.randomUUID(), 200), + new DistributionPlayer(UUID.randomUUID(), 150), + new DistributionPlayer(UUID.randomUUID(), 180) + }; + + // teamSize = 1, so both teams reach full capacity quickly + assertDoesNotThrow(() -> { + DistributionTeam[] result = splitter.compute(dTeams, dPlayers, new ArrayList<>(), 1, true, true); + assertNotNull(result); + }); + } + + @Test + void testSplitterBruteForce() { + Splitter splitter = new Splitter(); + DistributionTeam[] dTeams = new DistributionTeam[]{ + new DistributionTeam(Key.key("xerus", "red")), + new DistributionTeam(Key.key("xerus", "blue")) + }; + DistributionPlayer[] dPlayers = new DistributionPlayer[]{ + new DistributionPlayer(UUID.randomUUID(), 100), + new DistributionPlayer(UUID.randomUUID(), 200) + }; + + assertDoesNotThrow(() -> { + DistributionTeam[] result = splitter.compute(dTeams, dPlayers, new ArrayList<>(), 2, true, false); + assertNotNull(result); + }); + } +}