diff --git a/README.md b/README.md index 90788b5..e638553 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,114 @@ # pitiupi -Ferramenta **P2P** para práticas de Redes de Computadores: núcleo em Java com arquitetura de **plugins** (chat, compartilhamento de tela, exemplos multi-protocolo). +Ferramenta **P2P** para práticas de Redes de Computadores: núcleo em Java com arquitetura de **plugins** (chat, compartilhamento de tela, chat multi-protocolo). -**Documentação completa:** [docs/README.md](docs/README.md) +**Documentação completa:** [docs/README.md](docs/README.md) · **Quickstart:** [docs/documentation/00_quickstart.md](docs/documentation/00_quickstart.md) + +--- + +## Início rápido (um comando) + +Na **raiz do repositório**: + +```bash +chmod +x scripts/pitiupi.sh # só na primeira vez +./scripts/pitiupi.sh run --all +``` + +Compila o núcleo, os plugins do [catálogo](plugins.registry.json), instala JARs em `pitiupi/plugins/` e abre a aplicação. + +1. Login: **nome** e **porta** (ex.: `5000`). +2. Menus dos plugins: barra **Plugin → Social | Jogos | Streaming** (cada plugin registra a categoria no código). +3. P2P no mesmo PC: segunda instância com **porta diferente** (ex.: `5001`). + +```bash +./scripts/pitiupi.sh list # ids, categorias, JAR buildado? +./scripts/pitiupi.sh check # ferramentas + catálogo +``` + +--- + +## Catálogo de plugins (`plugins.registry.json`) + +Novos módulos entram no repositório adicionando uma entrada no JSON (não é preciso editar o script): + +| Campo | Significado | +| --- | --- | +| `id` | Usado em `--plugins chat,meuJogo` | +| `dir` | Pasta do módulo (ex.: `pitiupi.xadrez`) | +| `build` | `ant` ou `make` | +| `jar` | Caminho do JAR relativo a `dir` | +| `menuCategory` | `Social`, `Jogos` ou `Streaming` — filtro CLI e documentação | + +Plugins de referência hoje: + +| id | Categoria (menu) | Pasta | +| --- | --- | --- | +| `chat` | Social | `pitiupi.chat` | +| `mpchat` | Social | `pitiupi.mpChat` | +| `screencast` | Streaming | `pitiupi.screenCast` | + +A **categoria na interface** vem do `MenuCategory` no Java (`guiManager.createMenu(...)`), não do nome do JAR. O campo `menuCategory` no JSON alinha build/link com a doc. + +```bash +./scripts/pitiupi.sh run --all +./scripts/pitiupi.sh run --category Social +./scripts/pitiupi.sh run --plugins chat,screencast +./scripts/pitiupi.sh discover # pastas pitiupi.* com SPI ainda não no catálogo +``` + +--- + +## Novo plugin (resumo) + +1. Copiar estrutura de `pitiupi.chat` → `pitiupi.meuPlugin` ([tutorial](docs/documentation/10_tutorial_plugin.md)). +2. Em `createPlugin`, registrar menu: `guiManager.createMenu(MenuCategory.Jogos, ...)` (ou Social / Streaming). +3. Adicionar entrada em [`plugins.registry.json`](plugins.registry.json). +4. `./scripts/pitiupi.sh run --plugins meuId`. + +O núcleo carrega **todos os `*.jar`** em `pitiupi/plugins/` (pasta única). Detalhes: [pitiupi/plugins/README.md](pitiupi/plugins/README.md). + +--- ## Pré-requisitos | Ferramenta | Uso | | --- | --- | -| **JDK** com `javac` | Compilação (OpenJDK **25** recomendado; núcleo compila com target **17**) | -| **Apache Ant** 1.10+ | `pitiupi/`, `pitiupi.chat/`, `pitiupi.screenCast/` | -| **GNU Make** | `pitiupi.mpChat/` | - -Exemplo Ubuntu / WSL: +| **JDK** com `javac` | Compilação (OpenJDK **25** recomendado) | +| **Apache Ant** | Módulos NetBeans | +| **GNU Make** | `pitiupi.mpChat` | +| **python3** | Script `scripts/pitiupi.sh` (ler o catálogo) | ```bash sudo apt install openjdk-25-jdk ant make -javac -version # deve existir — JRE sozinho não basta +./scripts/pitiupi.sh check ``` -## Módulos +**GUI:** Swing — WSLg / `DISPLAY=:0`. -| Pasta | Descrição | Build | -| --- | --- | --- | -| `pitiupi/` | Núcleo (GUI, UDP multicast, TCP, heartbeat) | `ant jar` | -| `pitiupi.chat/` | Plugin referência — chat | `ant jar` | -| `pitiupi.screenCast/` | Plugin referência — screen cast | `ant jar` | -| `pitiupi.mpChat/` | Plugin referência — chat multicast + TCP | `make` | +--- -Compile cada módulo no seu diretório (núcleo primeiro). Instale os JARs dos plugins em `pitiupi/plugins/` — ver [pitiupi/plugins/README.md](pitiupi/plugins/README.md). +## Comandos do script -Exemplo de symlinks (Linux): +| Comando | Ação | +| --- | --- | +| `run` | Build + `plugins/` + app | +| `build` | Build + `plugins/` | +| `link` | Só symlinks (JARs já existem) | +| `list` | Catálogo e status do JAR | +| `discover` | Sugestão de módulos `pitiupi.*` | +| `check` | PATH + JSON válido | -```bash -cd pitiupi/plugins -ln -sf ../../pitiupi.chat/dist/pitiupi.chat.jar pitiupi.chat.jar -ln -sf ../../pitiupi.screenCast/dist/pitiupi.screenCast.jar pitiupi.screenCast.jar -ln -sf ../../pitiupi.mpChat/dist/mpChat.jar mpChat.jar -``` +Opções: `--all` (padrão), `--plugins ids`, `--category Social|Jogos|Streaming`. -Execute o núcleo (a partir de `pitiupi/`): +--- -```bash -cd pitiupi -java -jar dist/pitiupi.jar -``` +## Teste em rede -Quickstart: [docs/documentation/00_quickstart.md](docs/documentation/00_quickstart.md). -Trabalho prático (plugin próprio): [docs/documentation/10_tutorial_plugin.md](docs/documentation/10_tutorial_plugin.md). +Duas instâncias, portas distintas, mesma LAN. Ver [docs/documentation/15_testes_e_gate.md](docs/documentation/15_testes_e_gate.md) e [appendix_portas_e_multicast.md](docs/documentation/appendix_portas_e_multicast.md). + +--- + +## Origem + +Projeto base UFSJ (Redes). GitHub: [GabrielFSSantos/pitiupi](https://github.com/GabrielFSSantos/pitiupi). diff --git a/docs/documentation/02_modulos_do_repositorio.md b/docs/documentation/02_modulos_do_repositorio.md index 43a9b3f..d3682e1 100644 --- a/docs/documentation/02_modulos_do_repositorio.md +++ b/docs/documentation/02_modulos_do_repositorio.md @@ -2,6 +2,8 @@ ``` pitiupi/ # clone (este repositório) +├── plugins.registry.json # catálogo de módulos (build/link via scripts/pitiupi.sh) +├── scripts/pitiupi.sh ├── pitiupi/ # núcleo P2P (executável) ├── pitiupi.chat/ # plugin referência — chat ├── pitiupi.screenCast/ # plugin referência — captura de tela @@ -27,7 +29,7 @@ Todos dependem do JAR do núcleo no classpath de compilação. ## O que o aluno cria -Novo diretório irmão, por exemplo `pitiupi.xadrez/`, copiando a estrutura de `pitiupi.chat` (Ant + `META-INF/services`). +Novo diretório irmão, por exemplo `pitiupi.xadrez/`, copiando a estrutura de `pitiupi.chat` (Ant + `META-INF/services`). Registre o módulo em `plugins.registry.json` e use `MenuCategory` adequado no código. Não é obrigatório commitar o plugin do aluno neste repositório base; o professor pode definir fork ou entrega em zip. diff --git a/docs/documentation/10_tutorial_plugin.md b/docs/documentation/10_tutorial_plugin.md index ee9b7bd..1a05f3a 100644 --- a/docs/documentation/10_tutorial_plugin.md +++ b/docs/documentation/10_tutorial_plugin.md @@ -49,16 +49,50 @@ Arquivo `src/META-INF/services/pitiupi.models.Plugin`: pacote.MeuPlugin ``` -## 5. Build e instalação +## 5. Categoria no menu + +Escolha onde o plugin aparece na barra **Plugin**: + +| `MenuCategory` | Uso | +| --- | --- | +| `Social` | Chat, presença | +| `Jogos` | Jogos entre peers | +| `Streaming` | Screen cast, mídia | + +Exemplo: + +```java +guiManager.createMenu(MenuCategory.Jogos, new MeuMenu(...)); +``` + +## 6. Registrar no catálogo + +Adicione uma entrada em [`plugins.registry.json`](../../plugins.registry.json) na raiz do repo: + +```json +{ + "id": "xadrez", + "dir": "pitiupi.xadrez", + "build": "ant", + "jar": "dist/pitiupi.xadrez.jar", + "menuCategory": "Jogos" +} +``` + +O `id` é o que você passa para `./scripts/pitiupi.sh run --plugins xadrez`. + +## 7. Build e instalação ```bash -cd pitiupi.meuPlugin && ant jar -ln -sf ../../pitiupi.meuPlugin/dist/*.jar ../pitiupi/plugins/ +./scripts/pitiupi.sh build --plugins xadrez +# ou, manualmente: +cd pitiupi.xadrez && ant jar +ln -sf ../../pitiupi.xadrez/dist/pitiupi.xadrez.jar ../pitiupi/plugins/xadrez.jar ``` Reinicie o Pitiupi após trocar JARs. -## 6. Teste +## 8. Teste Duas instâncias, mesma rede, portas distintas se no mesmo host. Ver [00_quickstart.md](00_quickstart.md). diff --git a/docs/documentation/11_plugins_referencia.md b/docs/documentation/11_plugins_referencia.md index 0f8360e..b39173e 100644 --- a/docs/documentation/11_plugins_referencia.md +++ b/docs/documentation/11_plugins_referencia.md @@ -7,8 +7,9 @@ Três exemplos no repositório para comparar estilos de integração. Todos deve | | pitiupi.chat | pitiupi.screenCast | pitiupi.mpChat | | --- | --- | --- | --- | | Build | Ant | Ant | Make | +| id (`plugins.registry.json`) | `chat` | `screencast` | `mpchat` | | Classe | `ChatPlugin` | `ScreenCastPlugin` | `MultiProtocolChat` | -| Categoria menu | `Social` | (menu custom via `GuiManager`) | `Social` (multicast + unicast) | +| Categoria menu | `Social` | `Streaming` | `Social` (multicast + unicast) | | Mensagem | `ChatMessage` | `ScreenCastMessage` | `MpChatMessage` | | Transporte principal | UDP (via `ConnectionManager`) | UDP / payloads maiores | UDP público + **TCP privado** | | TCP avançado | Não | Depende da versão do módulo | `receivePrivateConnection`, `receiveBytes` | diff --git a/docs/documentation/15_testes_e_gate.md b/docs/documentation/15_testes_e_gate.md index ab1bc64..24caeda 100644 --- a/docs/documentation/15_testes_e_gate.md +++ b/docs/documentation/15_testes_e_gate.md @@ -9,7 +9,13 @@ Não há suíte JUnit integrada ao repositório. A validação principal é: ## Gate de build (recomendado) -Sequência: +Na raiz do clone (todos os módulos do catálogo): + +```bash +./scripts/pitiupi.sh build --all +``` + +Equivalente manual: ```bash cd pitiupi && ant jar @@ -18,7 +24,7 @@ cd ../pitiupi.screenCast && ant jar cd ../pitiupi.mpChat && make ``` -Falha se `java`, **`javac`**, Ant ou Make estiverem ausentes (instale `openjdk-*-jdk`, não só JRE). +Falha se `java`, **`javac`**, Ant, Make ou `python3` estiverem ausentes (instale `openjdk-*-jdk`, não só JRE). ## Checklist manual (rede) diff --git a/pitiupi.screenCast/src/pitiupi/screenCast/plugin/ScreenCastPlugin.java b/pitiupi.screenCast/src/pitiupi/screenCast/plugin/ScreenCastPlugin.java index f7dcd55..91b18ae 100644 --- a/pitiupi.screenCast/src/pitiupi/screenCast/plugin/ScreenCastPlugin.java +++ b/pitiupi.screenCast/src/pitiupi/screenCast/plugin/ScreenCastPlugin.java @@ -46,7 +46,7 @@ public class ScreenCastPlugin extends Plugin { @Override public void createPlugin(ConnectionManager connectionManager, GuiManager guiManager) { this.screenCastWindow = new ScreenCastWindow(connectionManager, guiManager); - guiManager.createMenu(MenuCategory.Social, new ScreenCastMenu(screenCastWindow)); + guiManager.createMenu(MenuCategory.Streaming, new ScreenCastMenu(screenCastWindow)); } @Override diff --git a/pitiupi/plugins/README.md b/pitiupi/plugins/README.md index d640687..77acaf8 100644 --- a/pitiupi/plugins/README.md +++ b/pitiupi/plugins/README.md @@ -1,18 +1,33 @@ # Plugins instalados -O núcleo carrega arquivos `.jar` desta pasta ao iniciar (relativo ao diretório de trabalho — execute `java -jar dist/pitiupi.jar` a partir de `pitiupi/`). +O núcleo carrega **todos os arquivos `.jar`** desta pasta ao iniciar. Execute sempre a partir de `pitiupi/`: -## Desenvolvimento +```bash +java -jar dist/pitiupi.jar +``` -Após compilar os módulos de referência, use symlinks (Linux): +## Pasta única (sem subpastas por categoria) + +Os JARs ficam lado a lado em `plugins/`. A divisão **chat / jogo / streaming** aparece no menu **Plugin → Social | Jogos | Streaming**, definida por cada plugin com `MenuCategory` no código. + +## Instalação automática (recomendado) + +Na raiz do repositório, o catálogo [`plugins.registry.json`](../../plugins.registry.json) lista módulos buildáveis: + +```bash +./scripts/pitiupi.sh link --all +./scripts/pitiupi.sh build --plugins chat +``` + +Symlinks gerados: `plugins/{id}.jar` → `../../{módulo}/{jar}`. + +## Manual ```bash cd pitiupi/plugins -ln -sf ../../pitiupi.chat/dist/pitiupi.chat.jar pitiupi.chat.jar -ln -sf ../../pitiupi.screenCast/dist/pitiupi.screenCast.jar pitiupi.screenCast.jar -ln -sf ../../pitiupi.mpChat/dist/mpChat.jar mpChat.jar +ln -sf ../../pitiupi.chat/dist/pitiupi.chat.jar chat.jar ``` -Ordem de build: núcleo (`pitiupi`) primeiro, depois cada plugin. Ver [docs/documentation/00_quickstart.md](../../docs/documentation/00_quickstart.md). +Não commite `.jar` — apenas este README e symlinks locais. -Os JARs gerados não devem ser commitados — apenas este README e symlinks locais. +Novo módulo: registrar no catálogo e ver [docs/documentation/10_tutorial_plugin.md](../../docs/documentation/10_tutorial_plugin.md). diff --git a/plugins.registry.json b/plugins.registry.json new file mode 100644 index 0000000..622c33a --- /dev/null +++ b/plugins.registry.json @@ -0,0 +1,25 @@ +{ + "plugins": [ + { + "id": "chat", + "dir": "pitiupi.chat", + "build": "ant", + "jar": "dist/pitiupi.chat.jar", + "menuCategory": "Social" + }, + { + "id": "screencast", + "dir": "pitiupi.screenCast", + "build": "ant", + "jar": "dist/pitiupi.screenCast.jar", + "menuCategory": "Streaming" + }, + { + "id": "mpchat", + "dir": "pitiupi.mpChat", + "build": "make", + "jar": "dist/mpChat.jar", + "menuCategory": "Social" + } + ] +} diff --git a/scripts/pitiupi.sh b/scripts/pitiupi.sh new file mode 100755 index 0000000..15144c2 --- /dev/null +++ b/scripts/pitiupi.sh @@ -0,0 +1,302 @@ +#!/usr/bin/env bash +# Build, instala plugins em pitiupi/plugins/ e executa o núcleo (catálogo: plugins.registry.json). +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +CORE="${REPO_ROOT}/pitiupi" +PLUGINS_DIR="${CORE}/plugins" +REGISTRY="${REPO_ROOT}/plugins.registry.json" + +usage() { + cat <<'EOF' +Uso: scripts/pitiupi.sh [opções] + +Comandos: + run Compila, atualiza plugins/ e abre a aplicação (padrão: --all) + build Compila e atualiza plugins/ + link Só symlinks em pitiupi/plugins/ (JARs já buildados) + list Lista plugins do catálogo (id, categoria, JAR presente) + discover Sugere entradas para o catálogo (pastas pitiupi.* com SPI) + check Ferramentas no PATH + valida plugins.registry.json + +Opções (exclusivas; padrão em run/build/link: --all): + --all Todos os plugins do catálogo + --plugins ids IDs separados por vírgula (ex.: chat,screencast) + --category Nome Social | Jogos | Streaming + +Exemplos: + ./scripts/pitiupi.sh list + ./scripts/pitiupi.sh run --all + ./scripts/pitiupi.sh run --category Social + ./scripts/pitiupi.sh run --plugins chat +EOF +} + +registry_python() { + python3 - "$REGISTRY" "$REPO_ROOT" "$@" <<'PY' +import json +import sys +from pathlib import Path + +registry_path = Path(sys.argv[1]) +repo = Path(sys.argv[2]) +mode = sys.argv[3] + +data = json.loads(registry_path.read_text(encoding="utf-8")) +plugins = data.get("plugins", []) + +def by_id(pid): + for p in plugins: + if p["id"] == pid: + return p + return None + +def jar_path(entry): + return repo / entry["dir"] / entry["jar"] + +if mode == "validate": + if not plugins: + print("ERRO: nenhum plugin no catálogo", file=sys.stderr) + sys.exit(1) + for p in plugins: + for key in ("id", "dir", "build", "jar", "menuCategory"): + if key not in p: + print(f"ERRO: campo ausente {key} em {p}", file=sys.stderr) + sys.exit(1) + sys.exit(0) + +if mode == "list": + for p in plugins: + jp = jar_path(p) + built = "sim" if jp.is_file() else "não" + print(f"{p['id']}\t{p['menuCategory']}\t{p['dir']}\tjar:{built}") + sys.exit(0) + +if mode == "discover": + known_dirs = {p["dir"] for p in plugins} + for child in sorted(repo.glob("pitiupi.*")): + if not child.is_dir(): + continue + spi = list(child.rglob("META-INF/services/pitiupi.models.Plugin")) + if not spi: + continue + status = "registrado" if child.name in known_dirs else "sugerido" + build = "make" if (child / "Makefile").is_file() else "ant" + print(f"{status}\t{child.name}\tbuild:{build}") + sys.exit(0) + +if mode == "resolve": + filter_mode = sys.argv[4] + arg = sys.argv[5] if len(sys.argv) > 5 else "" + + selected = [] + if filter_mode == "all": + selected = plugins + elif filter_mode == "category": + cat = arg.strip() + selected = [p for p in plugins if p["menuCategory"] == cat] + if not selected: + print(f"ERRO: categoria vazia ou inválida: {cat}", file=sys.stderr) + sys.exit(1) + elif filter_mode == "plugins": + ids = [x.strip() for x in arg.lower().split(",") if x.strip()] + for i in ids: + p = by_id(i) + if not p: + print(f"ERRO: id desconhecido no catálogo: {i}", file=sys.stderr) + sys.exit(1) + selected.append(p) + else: + print(f"ERRO: modo resolve {filter_mode}", file=sys.stderr) + sys.exit(1) + + for p in selected: + print(p["id"]) + sys.exit(0) + +if mode == "field": + pid = sys.argv[4] + field = sys.argv[5] + p = by_id(pid) + if not p: + sys.exit(1) + print(p[field]) + sys.exit(0) + +print(f"ERRO: modo desconhecido {mode}", file=sys.stderr) +sys.exit(1) +PY +} + +resolve_filter_mode() { + if [[ -n "${FILTER_PLUGINS:-}" ]]; then + echo "plugins" + elif [[ -n "${FILTER_CATEGORY:-}" ]]; then + echo "category" + else + echo "all" + fi +} + +resolve_filter_arg() { + local mode="$1" + case "$mode" in + plugins) echo "${FILTER_PLUGINS}" ;; + category) echo "${FILTER_CATEGORY}" ;; + all) echo "" ;; + esac +} + +selected_ids() { + local mode arg + mode="$(resolve_filter_mode)" + arg="$(resolve_filter_arg "$mode")" + registry_python resolve "$mode" "$arg" +} + +build_core() { + (cd "${REPO_ROOT}/pitiupi" && ant jar) +} + +build_one_plugin() { + local id="$1" + local dir build_cmd + dir="$(registry_python field "$id" dir)" + build_cmd="$(registry_python field "$id" build)" + case "$build_cmd" in + ant) (cd "${REPO_ROOT}/${dir}" && ant jar) ;; + make) (cd "${REPO_ROOT}/${dir}" && make) ;; + *) + printf 'ERRO: build desconhecido para %s: %s\n' "$id" "$build_cmd" >&2 + exit 1 + ;; + esac +} + +build_plugins_from_selection() { + local id + while IFS= read -r id; do + [[ -z "$id" ]] && continue + build_one_plugin "$id" + done < <(selected_ids) +} + +link_plugins_from_selection() { + local id dir jar_rel link_name + mkdir -p "${PLUGINS_DIR}" + rm -f "${PLUGINS_DIR}/"*.jar + + while IFS= read -r id; do + [[ -z "$id" ]] && continue + dir="$(registry_python field "$id" dir)" + jar_rel="$(registry_python field "$id" jar)" + link_name="${id}.jar" + ln -sf "../../${dir}/${jar_rel}" "${PLUGINS_DIR}/${link_name}" + done < <(selected_ids) + + local count + count="$(find "${PLUGINS_DIR}" -maxdepth 1 -name '*.jar' 2>/dev/null | wc -l)" + if [[ "$count" -eq 0 ]]; then + printf 'AVISO: nenhum plugin em %s.\n' "${PLUGINS_DIR}" >&2 + else + printf 'Plugins ativos em %s:\n' "${PLUGINS_DIR}" + ls -1 "${PLUGINS_DIR}"/*.jar + fi +} + +check_tools() { + local ok=0 + for cmd in java javac ant make python3; do + if command -v "${cmd}" >/dev/null 2>&1; then + printf 'OK %s\n' "${cmd}" + else + printf 'ERRO: %s não está no PATH\n' "${cmd}" >&2 + ok=1 + fi + done + if [[ ! -f "${REGISTRY}" ]]; then + printf 'ERRO: catálogo ausente: %s\n' "${REGISTRY}" >&2 + ok=1 + else + registry_python validate + printf 'OK plugins.registry.json\n' + fi + return "${ok}" +} + +CMD="run" +FILTER_PLUGINS="" +FILTER_CATEGORY="" +USE_ALL=1 + +while [[ $# -gt 0 ]]; do + case "$1" in + run | build | link | check | list | discover) + CMD="$1" + shift + ;; + --all) + USE_ALL=1 + FILTER_PLUGINS="" + FILTER_CATEGORY="" + shift + ;; + --plugins) + USE_ALL=0 + FILTER_PLUGINS="${2:-}" + FILTER_CATEGORY="" + shift 2 + ;; + --category) + USE_ALL=0 + FILTER_CATEGORY="${2:-}" + FILTER_PLUGINS="" + shift 2 + ;; + -h | --help) + usage + exit 0 + ;; + *) + printf 'Argumento desconhecido: %s\n' "$1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +if [[ "$USE_ALL" -eq 1 && -z "$FILTER_PLUGINS" && -z "$FILTER_CATEGORY" ]]; then + : # default --all +elif [[ -n "$FILTER_PLUGINS" && -n "$FILTER_CATEGORY" ]]; then + printf 'ERRO: use apenas um de --plugins, --category ou --all\n' >&2 + exit 1 +fi + +case "${CMD}" in + check) + check_tools + ;; + list) + registry_python list + ;; + discover) + registry_python discover + ;; + link) + link_plugins_from_selection + ;; + build) + check_tools || exit 1 + build_core + build_plugins_from_selection + link_plugins_from_selection + ;; + run) + check_tools || exit 1 + build_core + build_plugins_from_selection + link_plugins_from_selection + cd "${CORE}" + exec java -jar dist/pitiupi.jar + ;; +esac