Pular para o conteúdo

Catálogo de demonstração. Produtos, criadores, preços e avaliações são fictícios. Recursos gratuitos podem ser adquiridos de verdade; itens pagos não são cobrados nem simulados. Ver o que já funciona

Plataformas

O mesmo .jar roda nas seis. A detecção é por reflexão, sem compilar contra API nenhuma.

PlataformaThread principalComo o SDK despacha
PapersimBukkit.getScheduler().runTask
SpigotsimBukkit.getScheduler().runTask
Purpursimigual ao Paper (detectado como Paper)
Folianão existeGlobalRegionScheduler.execute
Velocitynão existeexecuta direto — não há para onde voltar
BungeeCordsimProxyServer.getScheduler().runAsync

Por que reflexão

A alternativa seria compilar contra paper-api, velocity-api e bungeecord-api ao mesmo tempo — e um plugin de Velocity carregaria classes do Bukkit que não existem lá, falhando com NoClassDefFoundError no primeiro uso.

Com reflexão, o mesmo artefato roda em todas, sem recompilar e sem um sub-artefato por plataforma. O custo é uma busca de classe no boot, feita uma vez e guardada.

Folia é um caso à parte

No Folia não existe “a thread principal”

O servidor é dividido em regiões, cada uma com a própria thread. Um Bukkit.getScheduler().runTask(...) lá lança UnsupportedOperationException em boa parte dos casos.

O equivalente correto é o GlobalRegionScheduler, usado para o que não pertence a nenhuma região — e o estado de licença é exatamente isso.

Por isso a detecção testa Folia antes de Bukkit: o Folia também tem as classes do Bukkit, e checar na ordem errada escolheria o agendador que não funciona.

Declarar suporte a Folia

plugin.yml
name: SeuPlugin
version: '1.0.0'
main: com.seupacote.SeuPlugin
api-version: '1.21'

# Só declare isto se o seu plugin REALMENTE respeitar os agendadores por
# região. Declarar sem respeitar produz corrupção silenciosa de estado.
folia-supported: true

Uso

java
// Uma vez, no onEnable:
var mainThread = PlatformSupport.dispatcher(this);

// Em qualquer callback do SDK:
mainThread.run(() -> {
    // Aqui a API do servidor pode ser tocada com segurança,
    // em qualquer uma das seis plataformas.
    Bukkit.broadcastMessage("Licença autorizada.");
});

No Velocity, passe o ProxyServer em vez do plugin — é ele que tem o agendador. Quando a plataforma não é reconhecida, o despachante executa na thread chamadora: correto para teste e ferramenta de linha de comando, e seguro, porque nada é silenciosamente descartado.

Velocity e BungeeCord

O que muda em um proxy:

  • Não há thread de tick no Velocity. Ele é inteiramente assíncrono; o despachante executa direto.
  • O logger é SLF4J, injetado. Não há getLogger() herdado.
  • A pasta de dados vem por injeção (@DataDirectory) e pode não existir ainda — crie antes de entregar ao SDK.
java
@Plugin(id = "seu-plugin", name = "Seu Plugin", version = "1.0.0")
public final class SeuPluginVelocity {

    private final ProxyServer proxy;
    private final Logger logger;
    private final Path dataDirectory;
    private LodestoneLicensing licensing;

    @Inject
    public SeuPluginVelocity(ProxyServer proxy, Logger logger, @DataDirectory Path dataDirectory) {
        this.proxy = proxy;
        this.logger = logger;
        this.dataDirectory = dataDirectory;
    }

    @Subscribe
    public void onProxyInitialize(ProxyInitializeEvent event) {
        Files.createDirectories(dataDirectory);

        licensing = LodestoneLicensing.builder()
                .productId(PRODUCT_ID)
                .licenseKey(lerChave())
                .dataFolder(dataDirectory)
                .platform(PlatformSupport.detect().id())
                .serverVersion(proxy.getVersion().getVersion())
                .build();

        licensing.activateAsync();
    }

    @Subscribe
    public void onProxyShutdown(ProxyShutdownEvent event) {
        if (licensing != null) licensing.close();
    }
}

Proxy e vínculo por IP não combinam

Em uma rede com proxy, o IP que o plugin do backend enxerga é o do proxy, e vários backends compartilham o mesmo. Vincular por IP nesse cenário não distingue nada. Use o vínculo por instalação — é o padrão justamente por isso.

O que não muda

Tudo que importa: o mesmo productId, a mesma chave, a mesma verificação de assinatura, o mesmo modo offline, o mesmo close(). O SDK não tem variante por plataforma — e não ter é o ponto.