Skip to content

Containers

Mariusz Matyszczak edited this page Jun 14, 2023 · 4 revisions

This page talks about the AbstractContainer class. Containers are useful to control and manipulate data.
At its nature, Containers are designed to be working with Java objects <-> JSON objects. What this really means is that you don't have to care much about creating serializers and deserializers; the library does it for you, of course you still have follow some common rules.

As of right now, Plus provides only two default implementations. Both for file saving and loading.

Set Up

Containers similarly like commands and menus have its registry system. It's important you create a valid manager for them.
You should do that in your main plugin class, as after registration of all containers, you'd want to hook a plugin instance to it.

public class YourPlugin extends JavaPlugin {
    private ContainerManager containerManager;

    @Override
    public void onEnable() {
        this.containerManager = new ContainerManager(); // First, initialize your manager.

        // now register your data containers
        this.containerManager.register("players", new PlayersContainer());

        // at the end hook your plugin to it
        this.containerManager.initialize(this);
    }
}

Examples

As mentioned before, containers are mainly meant to be working with files, although it's possible to add implementations for all sorts of databases like SQL, MongoDB or even Redis. Regardless, this page focuses only on the basic file implementations.

SingleContainer

Let's say we want to create a configuration file for our plugin. This means we'll need only a singular file.
Example on how to make a custom whitelist using SingleContainer.

/**
* This is the structure of our container.
*/
public class ServerContainer extends SingleContainer<ServerProperties> { // specify type of data
   
    public ServerContainer() {
        // Pass the correct type, as well as, provide your desired file name
        super(ServerProperties.class, "server_configuration");
    }

    @Override
    protected @NotNull ServerProperties emptyValue() {
        return new ServerProperties(new ArrayList<>()); // it's a must to add a default value
    }

    /**
    * This is our custom data. This object will be serialized and deserialized.
    */
    public static class ServerProperties {
        private final List<String> whitelistedPlayers;
        private boolean whitelist = false;

        // default constructor 
        private ServerProperties(@NotNull List<String> whitelistedPlayers) {
            this.whitelistedPlayers = whitelistedPlayers;
        }
        
        public List<String> getWhitelistedPlayers() {
            return this.whitelistedPlayers;
        }

        public void setWhitelist(boolean whitelist) {
            this.whitelist = whitelist;
        }
    }
}

MapContainer

This type of container is pretty much like a simple HashMap, a key-value-based cache. Let's say you need to store per-player data or per-team data, this is exactly what you need! A single key represents a unique id attached to a single file. The value is the content of that file (aka key).
Example on how to make custom per-player data container using MapContainer.

@InitialLoading // Used to load everything on plugin startup
public class PlayerDataContainer extends MapContainer<UUID, PlayerData> {
    public PlayerDataContainer() { // Default constructor
        super(
                UUID::toString, // We use UUID as the key, so we need to convert it to string
                UUID::fromString, // While reading from file, we need to convert it back to UUID
                PlayerData.class // We point to the class that we want to store
        );
    }

    /**
     * This method is called when the container is trying to get a value from the map.
     * We must create a default value for the key, in case it doesn't exist.
     *
     * @param key The key of the object.
     * @return A brand new instance of the object we want to store.
     */
    @Override
    protected @NotNull PlayerData emptyValue(@NotNull UUID key) {
        return new PlayerData(key); // Not to be confused, each player has its own instance of PlayerData.
    }

    /**
     * This class is used to store the data of the player, it gets serialized and deserialized automatically.
     * It can be pretty much anything, as long as you know how to use GSON serialization.
     * In this case, we are storing the UUID of the player and the amount of kills.
     */
    public static class PlayerData {
        private final UUID uuid;
        private int kills = 0;

        public PlayerData(UUID uuid) {
            this.uuid = uuid;
        }

        public void addKill() {
            this.kills++;
        }

        public UUID getUuid() {
            return uuid;
        }
    }
}
Clone this wiki locally