Skip to content
fren_gor edited this page Nov 22, 2021 · 22 revisions

Teams are one of the core features of UltimateAdvancementAPI. Teams are used everywhere, for example, advancement progressions are saved per-team.
Until now teams were not mentioned in the wiki to keep things simple, but to achieve the full potential of the API they are needed.

How teams work

By default, every player is in their own team, containing only they. When a new player joins, a new team is created and the player gets added to it.

Every player progression, as said before, is saved per-team. Thus, by default, every player has "their own advancement progression". If two players must have the same progression, one can be moved in the other's team, since players in the same team have the same progression.

Also, every team is identified by an integer id.

TeamProgression

TeamProgression objects stores information about a team. Furthermore, TeamProgression is used by the API to cache team information from the database. Thus, it can be used, for example, to get the components of a team.
TeamProgression has plenty of useful methods, which this page cannot cover all. Check them out on its Javadoc page.

Getting a TeamProgression instance

The method UltimateAdvancementAPI#getTeamProgression(Player player) is used to get the TeamProgression of a player team. However, if that player is not online, a UserNotLoadedException will be thrown.

To avoid this, the player information have to be loaded from the database. This is done through UltimateAdvancementAPI#loadOfflinePlayer(UUID uuid). This will load the player from the database and will keep they loaded even if the player logs on and then quits. Therefore, after having done with the TeamProgression, always remember to unload they using UltimateAdvancementAPI#unloadOfflinePlayer(UUID uuid) to avoid memory leaks.

Note that, due to how the caching system is implemented, only one TeamProgression instance is made per team, so calling UltimateAdvancementAPI#getTeamProgression(...) on two different players of the same team will return the same object.

There is also a more complex version of the loading method, which makes customizable what to do with the loaded TeamProgression:
UltimateAdvancementAPI#loadOfflinePlayer(UUID uuid, CacheFreeingOption option, Consumer<ObjectResult<TeamProgression>> action).

Let's see it in detail. CacheFreeingOption is used to choose what will be done with the loaded team. It can be:

  1. Loaded but not cached (CacheFreeingOption#DONT_CACHE());
  2. Loaded and automatically unloaded after a certain amount of ticks (CacheFreeingOption#AUTOMATIC(Plugin requester, long ticks));
  3. Loaded and kept in cache until a manual unload happens (CacheFreeingOption#MANUAL(Plugin requester)).

Note that the third option is the one used by the simpler version of the method seen before (UltimateAdvancementAPI#unloadOfflinePlayer(UUID uuid)).

Note that for the first and second options no manual unload have to be done.

The last parameter to pass is a Consumer<ObjectResult<TeamProgression>>, which provides the code that will be run when the loading will be completed (it can be put to null if no particular code is ment to be pass here). This is useful with the CacheFreeingOption#DONT_CACHE() option to retirve the TeamProgression object.
To go a bit deeper, the ObjectResult<TeamProgression> object holds the result of the request to the database, so whether it failed and the TeamProgression in case of success.

Example usage:

// This prints to console the uuid of every team component

// uuid of a random player (suppose they to be offline)
UUID uuid = UUID.fromString("831e3f14-b54d-47fe-bd4e-06d9e6ce681b");
api.loadOfflinePlayer(uuid, CacheFreeingOption.DONT_CACHE(), result -> {
    if (result.isSucceeded()) {
        TeamProgression progression = result.getResult();
        progression.forEachMember(memberUUID -> {
            System.out.println(memberUUID);
        });
    } else {
        // An error occurred
        result.getOccurredException().printStackTrace();
    }
});

Adding a player to a team

In order to add a player to a team, the player (which is, by default, in their own one) have to be moved in the final team.
This can be done by the method UltimateAdvancementAPI#updatePlayerTeam(Player playerToMove, Player aDestTeamPlayer).
The first player is the one to move, whereas the second is a player of the destination team.

If a result about the moving is needed, use UltimateAdvancementAPI#updatePlayerTeam(Player playerToMove, Player aDestTeamPlayer, Consumer<Result> action) instead.
The code to execute is provided by Consumer<Result>. Result is very similar to the ObjectResult from before, except it doesn't provide an object as result (in the example from the above section, it was the TeamProgression). It only provides whether the request succeded and an exception if it failed.

Example usage:

// This moves the first player in the second's team

Player playerOne = Bukkit.getPlayer("fren_gor");
Player playerTwo = Bukkit.getPlayer("EscanorTargaryen");

api.updatePlayerTeam(playerOne, playerTwo, result -> {
    if (result.isSucceeded()) {
        playerOne.sendMessage("You're now part of " + playerTwo.getName() + " team!");
    } else {
        // An error occurred
        playerOne.sendMessage("Something wrong happened while moving to " + playerTwo.getName() + " team");
        result.getOccurredException().printStackTrace();
    }
});

Removing a player from a team

To remove a player from a team, the player have to be moved in a new team.
This can be done using the method UltimateAdvancementAPI#movePlayerInNewTeam(Player playerToMove).

A version with a Consumer<ObjectResult<TeamProgression>> parameter is also present.

Example usage:

// This removes a player from its current team

Player playerOne = Bukkit.getPlayer("fren_gor");

api.movePlayerInNewTeam(playerOne, result -> {
    if (result.isSucceeded()) {
        TeamProgression progression = result.getResult();
        playerOne.sendMessage("You're now alone! (Team id is " + progression.getTeamId() + ")");
    } else {
        // An error occurred
        playerOne.sendMessage("Sorry, an error occurred. Stay in your team!");
        result.getOccurredException().printStackTrace();
    }
});

Team events

There are some events related to teams, listed here down below:

  • TeamLoadEvent - Called when a new TeamProgression is created and stored into the caching system (so when the first player of a team is loaded or joins the server);
  • TeamUnloadEvent - Called when a TeamProgression is removed from caching system (so when every player of a team is unloaded or logs off);
  • TeamUpdateEvent - Called when a player joins or leaves a team.

TeamProgression Best Practices

Due to the fact TeamProgression is used by the caching system, there are some limitations on its usage. For example, imagine that a couple of players, which are part of the same team, are online and a plugin takes a reference to the TeamProgression of that team. Suppose now that they all go offline.

Remember that a TeamProgression is present in the cache until every player of that team logs off.

Thus, when one of them comes back online, the API creates a new instance of TeamProgression and reload team data from the database. Thus, the plugin form before (that kept a reference to the old TeamProgression) now may have incorrect (and not up-to-date) information about the team!

So, it is always important to follow this set of rules when working with TeamProgressions:

  • It is prefereable to get a fresh reference to the TeamProgression of a team using UltimateAdvancementAPI#getProgression(...) everytime rather than storing that in a variable. If a variable is needed, it should store the team's id, which remains always valid. However, if storing a TeamProgression instance is very necessary, it is suggested to listen to the TeamUnloadEvent and remove the reference when it gets called;
  • Always make sure a player is loaded in the API before calling any update on an advancement, since that will eventually call the getProgression(...) method, which will result in a UserNotLoadedException;
  • Always make sure to unload a "manually" loaded player, not doing this may cause a memory leak!
  • If a long operation should be done on a team, make sure to load "manually" at least one player of that team. This way, the team will never be unloaded during the operation (and keeping the TeamProgression in cache).