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étodo | Retorno | Descrição |
|---|---|---|
getName() | String | Nome 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, comoMinhaEconomia.player: nome do jogador.amount: quantia da operação, quando aplicável.econ: instância denet.milkbowl.vault.economy.Economy, utilizada pela integraçãoMoneydo Vault. Para a economia personalizada deste guia, passenull.
Métodos com CompletableFuture
| Método | Retorno |
|---|---|
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étodo | Retorno |
|---|---|
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.