LogoyStore

Economias personalizadas

Aprenda a registrar sua própria economia no yPlugins e utilizá-la pelo PointsManager.

O yPlugins permite integrar uma economia própria implementando a interface EconomyProvider e registrando sua instância no sistema de serviços do Bukkit. Depois do registro, o PointsManager encontra o provedor pelo nome e encaminha as consultas e transações para ele.

Preparação

Adicione o yPlugins ao seu projeto como dependência provided ou compileOnly, conforme o guia de API dos Plugins. Utilize a interface fornecida pelo yPlugins instalado no servidor, sem incluir outra cópia dela no seu JAR.

Se o seu plugin é carregado pelo loader, consulte Desenvolva com o yPlugins. Nos exemplos de ciclo de vida abaixo, a classe principal estende BukkitPlugin e utiliza getLoader() quando o Bukkit exige um Plugin.

Para um plugin independente que estende JavaPlugin, declare o yPlugins como dependência de carregamento no seu plugin.yml:

# Carrega o yPlugins antes do seu plugin.
depend: [yPlugins]

O exemplo de referência utiliza assinaturas antigas. Na API atual, as operações retornam CompletableFuture, e a interface também exige os métodos set(...) e retrieveTop().

1. Conheça o contrato do provedor

Importe a interface de economia do yPlugins:

import br.com.ystoreplugins.product.economy.EconomyProvider;

Ela possui os seguintes métodos:

MétodoRetornoDescrição
getName()StringNome usado para identificar a economia.
has(String player, double amount)CompletableFuture<Boolean>Consulta se o jogador possui a quantia.
get(String player)CompletableFuture<Double>Consulta o saldo do jogador.
add(String player, double amount)CompletableFuture<Void>Adiciona a quantia ao saldo.
remove(String player, double amount)CompletableFuture<Void>Retira a quantia do saldo.
set(String player, double amount)CompletableFuture<Void>Define o saldo do jogador.
retrieveTop()CompletableFuture<HashMap<String, Double>>Retorna os nomes e saldos dos 10 maiores saldos.

O argumento player é o nome do jogador, e amount é a quantia da operação. Utilize valores não negativos e implemente a validação de saldo e a concorrência no serviço da sua economia.

Os métodos devem retornar um future válido. Para dados já disponíveis em memória, você pode usar CompletableFuture.completedFuture(...). Se a sua API já trabalha com futures, repasse o resultado dela e conclua o future somente quando a operação terminar.

Escolhendo o nome

O valor de getName() é o identificador que será passado ao PointsManager. Neste guia, utilizaremos MinhaEconomia.

Use um nome exclusivo, diferente dos identificadores das integrações nativas, como Money, PlayerPoints e yPoints. A comparação dos nomes dos provedores personalizados ignora maiúsculas e minúsculas.

O nome da economia pode ser diferente do nome do plugin. É esse identificador retornado por getName() que conecta o registro, a configuração e as chamadas ao PointsManager.

2. Implemente o EconomyProvider

O exemplo abaixo usa um serviço próprio chamado EconomiaService. Essa interface é ilustrativa: implemente seus métodos com a API, o cache ou a persistência da economia do seu plugin.

package com.exemplo.economia;

import java.util.HashMap;
import java.util.concurrent.CompletableFuture;

public interface EconomiaService {

    CompletableFuture<Double> getBalance(String player);

    CompletableFuture<Void> deposit(String player, double amount);

    CompletableFuture<Void> withdraw(String player, double amount);

    CompletableFuture<Void> setBalance(String player, double amount);

    CompletableFuture<HashMap<String, Double>> getTop10();
}

O provedor adapta esse serviço ao contrato do yPlugins:

package com.exemplo.economia;

import br.com.ystoreplugins.product.economy.EconomyProvider;

import java.util.HashMap;
import java.util.concurrent.CompletableFuture;

public final class MinhaEconomiaProvider implements EconomyProvider {

    private final EconomiaService service;

    public MinhaEconomiaProvider(EconomiaService service) {
        this.service = service;
    }

    @Override
    public String getName() {
        return "MinhaEconomia";
    }

    @Override
    public CompletableFuture<Boolean> has(String player, double amount) {
        return service.getBalance(player).thenApply(balance -> balance >= amount);
    }

    @Override
    public CompletableFuture<Double> get(String player) {
        return service.getBalance(player);
    }

    @Override
    public CompletableFuture<Void> add(String player, double amount) {
        return service.deposit(player, amount);
    }

    @Override
    public CompletableFuture<Void> remove(String player, double amount) {
        return service.withdraw(player, amount);
    }

    @Override
    public CompletableFuture<Void> set(String player, double amount) {
        return service.setBalance(player, amount);
    }

    @Override
    public CompletableFuture<HashMap<String, Double>> retrieveTop() {
        return service.getTop10();
    }
}

Cada operação delega para o serviço de saldo. Substitua essas chamadas pelas equivalentes da sua API. A consulta has(...) verifica o saldo naquele momento; a validação do débito deve fazer parte de withdraw(...) para lidar com transações concorrentes.

3. Registre a economia nos serviços do Bukkit

Depois de inicializar o serviço da sua economia, registre uma instância do provedor. O trecho abaixo fica dentro da sua classe principal que estende BukkitPlugin:

import br.com.ystoreplugins.product.economy.EconomyProvider;
import com.exemplo.economia.EconomiaService;
import com.exemplo.economia.MinhaEconomiaProvider;
import org.bukkit.Bukkit;
import org.bukkit.plugin.ServicePriority;

private EconomyProvider economyProvider;

private void registrarEconomia(EconomiaService service) {
    economyProvider = new MinhaEconomiaProvider(service);

    Bukkit.getServicesManager().register(
        EconomyProvider.class,
        economyProvider,
        getLoader(),
        ServicePriority.Normal
    );
}

@Override
public void onDisable() {
    if (economyProvider != null) {
        Bukkit.getServicesManager().unregister(EconomyProvider.class, economyProvider);
        economyProvider = null;
    }
}

Chame registrarEconomia(service) no seu fluxo de inicialização, quando o serviço estiver pronto. Registre a economia antes de os plugins consumidores tentarem utilizá-la.

Se a sua classe principal estende JavaPlugin, passe this como proprietário no lugar de getLoader(). Guarde a instância do provedor para remover esse registro no encerramento do seu plugin.

O PointsManager consulta os registros de EconomyProvider automaticamente e procura o nome correspondente a getName().

4. Utilize a economia pelo PointsManager

Importe a classe utilitária:

import com.ystoreplugins.ymodel.util.economy.method.PointsManager;

Os métodos são estáticos e recebem:

  • plugin: identificador da economia, como MinhaEconomia.
  • player: nome do jogador.
  • amount: quantia da operação, quando aplicável.
  • econ: instância de net.milkbowl.vault.economy.Economy, utilizada pela integração Money do Vault. Para a economia personalizada deste guia, passe null.

Métodos com CompletableFuture

MétodoRetorno
hasEco(String plugin, String player, double amount, Economy econ)CompletableFuture<Boolean>
getEco(String plugin, String player, Economy econ)CompletableFuture<Double>
addAsync(String plugin, String player, double amount, Economy econ)CompletableFuture<Void>
removeAsync(String plugin, String player, double amount, Economy econ)CompletableFuture<Void>

Para provedores personalizados, esses métodos permitem encadear as operações sem esperar o resultado com join().

Consultar saldo e verificar uma quantia

Os trechos que usam getLogger() ou getLoader() consideram que você está dentro de uma classe que estende BukkitPlugin.

PointsManager.getEco("MinhaEconomia", "Steve", null)
    .thenAccept(saldo -> getLogger().info("Saldo de Steve: " + saldo))
    .exceptionally(error -> {
        getLogger().warning("Falha ao consultar saldo: " + error.getMessage());
        return null;
    });

PointsManager.hasEco("MinhaEconomia", "Steve", 100.0, null)
    .thenAccept(possui -> getLogger().info("Possui 100 moedas: " + possui))
    .exceptionally(error -> {
        getLogger().warning("Falha ao verificar saldo: " + error.getMessage());
        return null;
    });

Adicionar e remover saldo

Neste exemplo, o débito é solicitado depois da conclusão do depósito, e o saldo é consultado depois do débito:

PointsManager.addAsync("MinhaEconomia", "Steve", 100.0, null)
    .thenCompose(ignored -> PointsManager.removeAsync("MinhaEconomia", "Steve", 25.0, null))
    .thenCompose(ignored -> PointsManager.getEco("MinhaEconomia", "Steve", null))
    .thenAccept(saldo -> getLogger().info("Saldo após as operações: " + saldo))
    .exceptionally(error -> {
        getLogger().warning("Falha na operação: " + error.getMessage());
        return null;
    });

addAsync(...) e removeAsync(...) retornam CompletableFuture<Void>: a API não fornece um objeto de transação nem um booleano de aprovação. O tratamento de saldo insuficiente e de falhas de persistência pertence à implementação do seu provedor.

Interagir com o Bukkit após uma consulta

Os callbacks de um future podem executar fora da thread principal. Em Bukkit/Spigot, agende ações como enviar mensagens ao jogador no scheduler do servidor:

import org.bukkit.Bukkit;
import org.bukkit.entity.Player;

private void mostrarSaldo(Player player) {
    String playerName = player.getName();

    PointsManager.getEco("MinhaEconomia", playerName, null)
        .thenAccept(saldo -> Bukkit.getScheduler().runTask(getLoader(), () -> {
            if (player.isOnline()) {
                player.sendMessage("§aSeu saldo: §f" + saldo);
            }
        }))
        .exceptionally(error -> {
            getLogger().warning("Falha ao mostrar saldo: " + error.getMessage());
            return null;
        });
}

Métodos síncronos

MétodoRetorno
has(String plugin, String player, double amount, Economy econ)boolean
get(String plugin, String player, Economy econ)double
add(String plugin, String player, double amount, Economy econ)void
remove(String plugin, String player, double amount, Economy econ)void
set(String plugin, String player, double amount, Economy econ)void
double saldo = PointsManager.get("MinhaEconomia", "Steve", null);
boolean possui = PointsManager.has("MinhaEconomia", "Steve", 100.0, null);

PointsManager.add("MinhaEconomia", "Steve", 50.0, null);
PointsManager.remove("MinhaEconomia", "Steve", 10.0, null);
PointsManager.set("MinhaEconomia", "Steve", 500.0, null);

Para provedores personalizados, os métodos síncronos aguardam os futures com join(). Prefira as versões com future quando sua economia faz consultas ou gravações demoradas, evitando bloquear a thread principal. As consultas hasEco e getEco de integrações nativas podem chamar a implementação síncrona diretamente.

Confirmar que o provedor está registrado

Você pode consultar os serviços pelo nome antes de utilizar uma economia:

import br.com.ystoreplugins.product.economy.EconomyProvider;
import org.bukkit.Bukkit;

EconomyProvider provider = Bukkit.getServicesManager()
    .getRegistrations(EconomyProvider.class)
    .stream()
    .map(registration -> registration.getProvider())
    .filter(registered -> registered.getName().equalsIgnoreCase("MinhaEconomia"))
    .findFirst()
    .orElseThrow(() -> new IllegalStateException("Economia não registrada: MinhaEconomia"));

Se o PointsManager não encontrar um provedor personalizado, as consultas retornam false ou 0, e as operações de alteração encerram sem modificar um saldo. Por isso, o registro precisa estar disponível antes do uso; um future de alteração concluído, isoladamente, não comprova que um provedor foi encontrado.

Definir saldo e consultar o TOP diretamente

O PointsManager possui set(...) síncrono. Para definir o saldo com um future, utilize set(...) do provedor. O TOP também é consultado diretamente por retrieveTop():

provider.set("Steve", 500.0)
    .thenCompose(ignored -> provider.retrieveTop())
    .thenAccept(top -> top.forEach((nome, saldo) -> {
        getLogger().info(nome + ": " + saldo);
    }))
    .exceptionally(error -> {
        getLogger().warning("Falha ao definir saldo ou consultar TOP: " + error.getMessage());
        return null;
    });

O resultado de retrieveTop() é um HashMap<String, Double> com os jogadores e seus saldos. A ordem de iteração do mapa não representa a classificação; ordene por saldo ao montar a exibição do ranking.

5. Configure a economia em um plugin compatível

Nos plugins que utilizam o PointsManager para resolver o campo provider, informe o mesmo nome retornado por getName().

Por exemplo, usando o formato de economies.yml do yAgiota:

# ID local da economia selecionada por padrão.
default: 'minha-moeda'

economies:
  minha-moeda:
    # Nome retornado pelo EconomyProvider registrado no Bukkit.
    provider: 'MinhaEconomia'
    # Nome e abreviação exibidos pelo plugin consumidor.
    display: 'Minha Moeda'
    abbreviated: 'moedas'
    # Habilite se o seu serviço permite operações com jogadores offline.
    allow-offline: true
    # Permissão para utilizar esta economia. Vazio permite a todos.
    permission: ''
    # Ícone de seleção da economia no yAgiota.
    item-settings:
      material: EMERALD
      name: '&aMinha Moeda'
      lore:
        - '&7Economia personalizada do servidor.'

Nesse exemplo, minha-moeda é o ID local usado em default, enquanto MinhaEconomia é o identificador do serviço registrado. Consulte a configuração de economias do plugin consumidor para seguir a estrutura e os campos exigidos por ele.

Nesta página