feat(messaging): PluginKind.PING no catálogo de transporte

Alinha núcleo com plugin Ping didático e evita NoSuchFieldError ao carregar ping.jar novo.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Gabriel Felipe Souza SantosandCursor committed 2026-10-27 20:13:22 -03:00
1 parent fc9739a998
commit 7c8a476253
4 files changed
+35 -22

No files matched your search

+1 -1
View File
@@ -38,6 +38,6 @@ Os plugins de referência são **exemplos de sala de aula**, não produtos compl
| --- | --- | --- | | --- | --- | --- |
| **chat** | texto, sala geral, unicast/multicast/TCP, TCP privado básico | imagens, moderação, histórico persistente, criptografia | | **chat** | texto, sala geral, unicast/multicast/TCP, TCP privado básico | imagens, moderação, histórico persistente, criptografia |
| **screencast** | stream read-only, avisos de sessão | controle remoto do mouse/teclado, áudio | | **screencast** | stream read-only, avisos de sessão | controle remoto do mouse/teclado, áudio |
| **ping** (exercício do aluno) | RTT, ping uma vez/contínuo, parar e avisar o peer | ICMP do SO, gráficos, SLA | | **ping** (exercício do aluno) | chrome de sessão v1 (`PluginRoutingPanel`, abas ↑/↓), RTT, UDP apenas | TCP privado, ICMP do SO, gráficos, SLA |
Para evoluir: copie o padrão (`Plugin` + mensagens + painel na aba) e acrescente features no **seu** JAR — sem inflar o núcleo. Para evoluir: copie o padrão (`Plugin` + mensagens + painel na aba) e acrescente features no **seu** JAR — sem inflar o núcleo.
+16 -19
View File
@@ -78,19 +78,18 @@ Use `PeerIpv4` e `LocalNetworkInfo.bestLocalIpv4()` para IP legível no Windows.
## Passo 5 — `PingPanel` (UI) ## Passo 5 — `PingPanel` (UI)
`JPanel` com: Siga o mesmo chrome do screencast: `PluginSessionWorkspace` + `PluginRoutingPanel` (`PluginKind.PING`, sem TCP privado).
- `SessionTabBar` — aba fixa **Todos** + `addTabIfAbsent` quando chega tráfego de um peer (**sem** mudar a aba ativa da shell). - **Destino e transporte** no topo (IP, forma de envio, banner da sessão).
- Toolbar: **Enviar ping** (uma vez) e **Ping contínuo** (toggle só em abas de peer **↑**; na sala **Todos** só o envio único).
- `SessionTabBar`: sala `Todos ↑`; peers que você abre `Nome ↑`; recebimento passivo `Nome ↓` (`view:<ip>`).
- `+ Nova` / `PeerDraftPanel` para IP manual.
- `JTextArea` monoespaçada por aba; linhas como `[HH:mm:ss] pong ← Alice · RTT 42 ms`. - `JTextArea` monoespaçada por aba; linhas como `[HH:mm:ss] pong ← Alice · RTT 42 ms`.
- Botão **Parar todos os pings contínuos** chamando o controller. - Abas **↓**: `bindReceiveSession` — controles de envio desabilitados.
**Não** use `JFrame`. No `PingPlugin`: **Não** use `JFrame`. No `PingPlugin`: `bindShellWorkspaceContent(panel)`.
```java A aba da shell só abre quando o usuário clicar na galeria **Plugins** ou numa peer action (sem enviar ping automaticamente).
bindShellWorkspaceContent(panel);
```
A aba só abre quando o usuário clicar na galeria **Plugins** ou numa peer action.
## Passo 6 — `PingPlugin` ## Passo 6 — `PingPlugin`
@@ -107,14 +106,12 @@ A aba só abre quando o usuário clicar na galeria **Plugins** ou numa peer acti
**Peer actions sugeridas:** **Peer actions sugeridas:**
| id | Label | Alvo | | id | Label | Alvo | Comportamento |
| --- | --- | --- | | --- | --- | --- | --- |
| `ping.once` | Ping uma vez | um peer | | `ping.peer` | Enviar ping para esta pessoa | um peer | Abre/foca o plugin e a aba `↑` com o IP — **sem** disparar ping |
| `ping.continuous` | Ping contínuo | um peer | | `ping.room` | Abrir ping na sala (Todos) | Todos | Abre a aba sala — **sem** disparar ping |
| `ping.stop` | Parar ping | um peer |
| `ping.all.once` | Ping para todos (uma vez) | Todos → `MULTICAST_UDP` |
Em cada ação de peer único: `workspace.openOrFocusTab(getIdentifier(), getDisplayName())` e chame o controller. O envio fica nos botões do painel.
`receiveMessage`: despache para o controller com `SwingUtilities.invokeLater`. `receiveMessage`: despache para o controller com `SwingUtilities.invokeLater`.
@@ -126,8 +123,8 @@ Em cada ação de peer único: `workspace.openOrFocusTab(getIdentifier(), getDis
2. Aba **Configurações** → **Instalar JAR…** (ou copie para `~/pitiupi/plugins/`). 2. Aba **Configurações** → **Instalar JAR…** (ou copie para `~/pitiupi/plugins/`).
3. **Reinicie** o Pitiupi (obrigatório após mudar plugins). 3. **Reinicie** o Pitiupi (obrigatório após mudar plugins).
4. Dois processos no mesmo PC: portas **5000** e **5001**, nomes diferentes. 4. Dois processos no mesmo PC: portas **5000** e **5001**, nomes diferentes.
5. Peer action **Ping uma vez** → veja RTT no log. 5. Lista lateral → **Enviar ping para esta pessoa** → aba `↑` com IP; clique **Enviar ping** → RTT no log.
6. **Ping contínuo** → linhas a cada ~1 s; **Parar ping** → o outro vê “parou o ping contínuo”. 6. Toggle **Ping contínuo** na aba `↑` → linhas a cada ~1 s; desligar o toggle → o outro vê “parou o ping contínuo” na aba `↓`.
7. Com colega na LAN: mesma porta, JAR instalado nos dois. 7. Com colega na LAN: mesma porta, JAR instalado nos dois.
Instalação usa o mesmo `PluginStore` da aba **Configurações** (`installFromFile` + reinício). O núcleo expõe `pitiupi.tools.PluginStoreCli` para testes sem interface gráfica. Instalação usa o mesmo `PluginStore` da aba **Configurações** (`installFromFile` + reinício). O núcleo expõe `pitiupi.tools.PluginStoreCli` para testes sem interface gráfica.
@@ -141,7 +138,7 @@ O chat usa a mesma ideia de abas de sessão, mas adiciona roteamento TCP privado
- [ ] SPI e `getIdentifier` únicos - [ ] SPI e `getIdentifier` únicos
- [ ] Três mensagens registradas - [ ] Três mensagens registradas
- [ ] RTT calculado no `Pong` - [ ] RTT calculado no `Pong`
- [ ] Aba passiva ao receber ping de peer novo - [ ] Aba passiva `↓` ao receber ping de peer novo (sem roubar foco da shell)
- [ ] Contínuo + parar com aviso ao peer - [ ] Contínuo + parar com aviso ao peer
- [ ] Reinício após instalar JAR - [ ] Reinício após instalar JAR
- [ ] Teste com segundo usuário (5001 ou outro PC) - [ ] Teste com segundo usuário (5001 ou outro PC)
@@ -9,7 +9,8 @@ public final class PluginTransportCatalog {
public enum PluginKind { public enum PluginKind {
CHAT, CHAT,
SCREEN_CAST SCREEN_CAST,
PING
} }
private PluginTransportCatalog() { private PluginTransportCatalog() {
@@ -22,7 +23,7 @@ public final class PluginTransportCatalog {
TransportMode.UNICAST_UDP, TransportMode.UNICAST_UDP,
TransportMode.TCP TransportMode.TCP
); );
case SCREEN_CAST -> List.of( case SCREEN_CAST, PING -> List.of(
TransportMode.MULTICAST_UDP, TransportMode.MULTICAST_UDP,
TransportMode.UNICAST_UDP TransportMode.UNICAST_UDP
); );
@@ -35,6 +36,7 @@ public final class PluginTransportCatalog {
TransportMode.UNICAST_UDP, TransportMode.UNICAST_UDP,
TransportMode.TCP TransportMode.TCP
); );
case PING -> List.of(TransportMode.UNICAST_UDP);
}; };
} }
} }
@@ -13,4 +13,18 @@ class PluginTransportCatalogTest {
assertTrue(modes.contains(TransportMode.MULTICAST_UDP)); assertTrue(modes.contains(TransportMode.MULTICAST_UDP));
assertFalse(modes.contains(TransportMode.TCP)); assertFalse(modes.contains(TransportMode.TCP));
} }
@Test
void pingBroadcastExcludesTcp() {
var modes = PluginTransportCatalog.forBroadcast(PluginTransportCatalog.PluginKind.PING);
assertTrue(modes.contains(TransportMode.MULTICAST_UDP));
assertFalse(modes.contains(TransportMode.TCP));
}
@Test
void pingSinglePeerIsUdpOnly() {
var modes = PluginTransportCatalog.forSinglePeer(PluginTransportCatalog.PluginKind.PING);
assertTrue(modes.contains(TransportMode.UNICAST_UDP));
assertFalse(modes.contains(TransportMode.TCP));
}
} }