Plugin profissional para Paper 1.21.x que implementa um sistema de Builds como Item com persistência em PostgreSQL, anti-dupe real via banco de dados, preview interativo com move/rotate e Block Display Entities.
- Preview interativo — painel clicável no chat com 6 direções de movimento + rotação 90°
- Rotação de blocos correta —
facingeaxisdo blockData rotacionam junto com as coordenadas (escadas, troncos, portas, camas, etc.) - Item removido ao usar — o item de build some do inventário após construção bem-sucedida
/structra delete— remove o item da mão sem apagar o registro no banco
- Sistema de build como item com UUID único por instância
- Anti-dupe via
SELECT FOR UPDATEem transação SERIALIZABLE - Preview via Block Display Entities
- Templates registrados automaticamente no banco ao criar com
/structra create - Botões clicáveis no chat para confirmar/cancelar preview
- Schematics em JSON+GZIP
StructraBuilds/
├── src/main/java/dev/structra/builds/
│ ├── StructraBuilds.java # Classe principal (bootstrap)
│ ├── StructraPlugin.java # Orquestrador / DI container
│ ├── model/
│ │ ├── BuildType.java # Enum: SHOP | PLAYER
│ │ ├── BuildStatus.java # Enum: UNUSED | USED
│ │ ├── BuildInstance.java # Entidade de build (UUID único)
│ │ ├── BuildTemplate.java # Template (registrado no banco)
│ │ ├── PlayerSelection.java # Seleção da Wand (pos1 + pos2)
│ │ └── SchematicBlock.java # Bloco relativo de schematic
│ ├── database/
│ │ ├── DatabaseConfig.java # Configuração do pool HikariCP
│ │ ├── DatabaseService.java # Pool HikariCP + auto-DDL
│ │ └── BuildRepository.java # CRUD + SELECT FOR UPDATE (anti-dupe)
│ ├── service/
│ │ ├── SchematicService.java # Captura/salva schematics (JSON+GZIP)
│ │ ├── CustomItemService.java # Cria/lê itens com PDC
│ │ ├── WandService.java # Lógica da ferramenta de seleção
│ │ ├── BuildService.java # Pipeline de construção assíncrono
│ │ └── PreviewService.java # Preview via Block Display Entities
│ ├── manager/
│ │ ├── SelectionManager.java # Estado de seleções da Wand
│ │ └── BuildManager.java # Máquina de estados: move/rotate/confirm
│ ├── listener/
│ │ ├── WandListener.java # Cliques da ferramenta wand
│ │ ├── BuildItemListener.java # Clique direito com item de build
│ │ ├── ChatConfirmListener.java # Fallback: digitar confirmar/cancelar
│ │ ├── AntiDupeListener.java # Proteção no inventário
│ │ └── PlayerQuitListener.java # Limpeza ao sair/trocar de mundo
│ ├── command/
│ │ ├── StructraCommand.java # /structra (subcomandos)
│ │ ├── ConfirmCancelCommand.java # /structra-confirm e /structra-cancel
│ │ └── PreviewControlCommand.java # /structra-move e /structra-rotate
│ ├── exception/
│ │ ├── DuplicateBuildException.java
│ │ ├── BuildNotFoundException.java
│ │ ├── SchematicNotFoundException.java
│ │ └── InvalidSelectionException.java
│ └── util/
│ ├── StructraLogger.java
│ ├── MessageUtil.java
│ └── AsyncUtil.java
├── src/main/resources/
│ ├── plugin.yml
│ ├── config.yml
│ └── schematics/ # Criado automaticamente
├── docker-compose.yml
├── .env.example
└── pom.xml
- Java 21+
- Maven 3.9+
- Docker + Docker Compose
- Servidor Paper 1.21.x
cd StructraBuilds
cp .env.example .env
docker-compose up -d
docker-compose ps # Esperado: postgres running, healthydatabase:
host: "localhost"
port: 5432
database: "structrabuilds"
username: "structra"
password: "structra_secret"
ssl: false
pool-size: 5As tabelas build_instances e build_templates são criadas automaticamente na primeira inicialização.
mvn clean package
cp target/StructraBuilds-1.0.1.jar /path/to/paper/plugins/| Comando | Permissão | Descrição |
|---|---|---|
/structra give <player> <id> |
structra.admin |
Dar build item |
/structra delete |
structra.use |
Remover item de build da mão (mantém registro no banco) |
/structra wand |
structra.wand |
Receber ferramenta de seleção |
/structra create <nome> |
structra.wand |
Salvar seleção → schematic + registro no banco automático |
/structra list |
structra.admin |
Listar templates |
/structra reload |
structra.admin |
Recarregar (lê templates do banco) |
Estes comandos são disparados pelos botões no chat e não precisam ser digitados manualmente:
| Comando | Descrição |
|---|---|
/structra-confirm |
Confirma e coloca o build |
/structra-cancel |
Cancela o preview |
/structra-move <f|b|l|r|u|d> |
Move o preview 1 bloco |
/structra-rotate |
Gira o preview 90° |
1. /structra wand → recebe a ferramenta
2. Clique esq/dir para selecionar → define pos1 e pos2
3. /structra create minha_casa → salva schematic + insere em build_templates
4. /structra list → já aparece o template
5. /structra give <jogador> minha_casa → dá o item e cria registro em build_instances
Não é necessário editar config.yml nem usar /structra reload — templates são registrados no banco automaticamente.
1. Jogador segura o item de build e clica com botão direito
2. Preview ghost da estrutura aparece na localização
3. Chat exibe painel de controle clicável:
─────────────────────────────────────
⚙ minha_casa X:10 Y:64 Z:20 ↻ 0°
[↑ Frente] [⬆ Cima]
[← Esq] [→ Dir]
[↓ Trás] [⬇ Baixo]
[↻ Girar] [✔ Confirmar] [✗ Cancelar]
─────────────────────────────────────
4. Cliques de movimento atualizam o preview em tempo real
5. [↻ Girar] rotaciona 90° (com rotação correta de facing/axis)
6. [✔ Confirmar] → blocos são colocados no mundo + item some do inventário
7. [✗ Cancelar] → preview removido, item mantido
Movimento: Frente/Trás/Esq/Dir são relativos ao olhar do jogador no momento do clique.
Rotação suportada: Escadas, portas, alçapões, baús, fornos, dispensers, troncos/madeiras, troncos pelados, correntes, basalto, camas e todos blocos com propriedade facing ou axis.
Duas camadas de proteção:
BuildRepository.claimBuild(UUID):
BEGIN TRANSACTION (SERIALIZABLE)
SELECT status FROM build_instances WHERE id=? FOR UPDATE ← bloqueia linha
if status == 'USED' → DuplicateBuildException
UPDATE build_instances SET status='USED', placed_at=NOW() WHERE id=?
COMMIT
← ENTÃO coloca os blocos no mundo
Mesmo com 100 cópias do item, apenas uma transação pode marcar o UUID como USED.
AntiDupeListener bloqueia:
CLONE_STACK(ctrl+clique criativo)COLLECT_TO_CURSOR- Drag para múltiplos slots
- Uso em crafting
| Permissão | Default | Descrição |
|---|---|---|
structra.use |
true |
Usar builds (colocar no mundo) |
structra.wand |
op |
Usar ferramenta de seleção |
structra.admin |
op |
Dar builds, reload, gerenciar templates |
structra.bypass.antidupe |
false |
Bypass anti-dupe (só para testes) |
# plugins/StructraBuilds/config.yml
database:
host: "seu-host-postgres.exemplo.com"
port: 5432
database: "structrabuilds_prod"
username: "structra_app"
password: "${STRUCTRA_DB_PASSWORD}"
ssl: true
pool-size: 10CREATE USER structra_app WITH PASSWORD 'senha_forte';
CREATE DATABASE structrabuilds_prod OWNER structra_app;
GRANT CONNECT ON DATABASE structrabuilds_prod TO structra_app;
GRANT USAGE ON SCHEMA public TO structra_app;
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO structra_app;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO structra_app;
-- NÃO dar DROP, DELETE ou CREATE ao usuário da aplicação| Players Online | pool-size |
|---|---|
| 0–50 | 5 |
| 50–200 | 10 |
| 200–500 | 15–20 |
| 500+ | 20–30 |
# crontab -e
0 3 * * * pg_dump -U structra_app -h localhost structrabuilds_prod | gzip > /backups/structrabuilds_$(date +%Y%m%d).sql.gz
0 4 * * * find /backups -name "structrabuilds_*.sql.gz" -mtime +30 -delete- ✅ Nunca expor porta 5432 publicamente — use VPN ou SSH tunnel
- ✅ Usar SSL para conexões remotas (
ssl: true) - ✅ Senha forte no config.yml (use variáveis de ambiente)
- ✅ Permissões mínimas no usuário do banco
- ✅ Backups automáticos diários
- ✅ Restringir
structra.bypass.antidupea accounts de teste apenas
- Upgrade de builds — tabela
build_templatescom colunanext_level_template_id - Produção automática — task scheduler periódico por
BuildInstancecolocada - GUI de gerenciamento —
InventoryHoldercustom por template - Integração com economia — Vault API em
BuildService - Venda entre jogadores — tabela
build_marketcom UUID do vendedor/comprador - Rede multi-servidor — substituir cache local por Redis (
JedisPool)
Contribuições são bem-vindas! Leia o CONTRIBUTING.md para saber como reportar bugs, sugerir funcionalidades e enviar Pull Requests.
Este projeto é licenciado sob a MIT License — veja o arquivo LICENSE para detalhes.