Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,10 @@ strings.csv
/55299*.txt
# Superpowers planning docs (local only, not source-controlled)
docs/superpowers/
# Installeur team : binaires livres (regenerables depuis _INSTALLEUR-TEAM/installer-src)
_INSTALLEUR-TEAM/Installateur-WoWsToolkit-Team.exe
_INSTALLEUR-TEAM/wows_toolkit.exe
# Logs de build locaux
build_*.log
# Sauvegardes patch locales
BACKUPS/
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Lancer WoWs Toolkit (team).bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@echo off
REM Lanceur WoWs Toolkit (build team : webhook + filtre Clan Wars)
REM Double-clique ce fichier pour lancer l'appli en restaurant automatiquement notre build.
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0lancer-toolkit.ps1"
45 changes: 45 additions & 0 deletions _INSTALLEUR-TEAM/LIRE-MOI-distribution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# 📦 Distribution de l'installeur aux potes

## Le fichier à envoyer

**`Installateur-WoWsToolkit-Team.exe`** (~83 Mo) — c'est le **seul fichier** à envoyer.
Il embarque la version modifiée du toolkit (envoi auto Discord + filtre Clan Wars,
auto-update neutralisé). Envoie-le par **WeTransfer** ou **Discord** (si la taille passe).

## Ce que le pote fait

1. Télécharge `Installateur-WoWsToolkit-Team.exe`
2. **Double-clique** dessus
- Windows SmartScreen peut afficher un avertissement (fichier non signé) :
→ *« Informations complémentaires » » « Exécuter quand même »*
3. L'installateur :
- copie l'appli dans `%LOCALAPPDATA%\WoWs Toolkit`
- crée un raccourci **« WoWs Toolkit (team) »** sur le bureau
4. Le pote lance le jeu via le **raccourci du bureau**

## Important à dire aux potes

- ⛔ **Si l'appli propose une mise à jour → la REFUSER.** L'auto-update est déjà
neutralisé dans cette version, mais qu'ils ne réinstallent pas la version officielle
par-dessus.
- 📤 Par défaut, **seules les parties Clan Wars** sont envoyées sur Discord. Réglage
via le menu **« 📤 Discord »** en haut de la fenêtre, ou l'onglet Paramètres.
- 🔌 Le **lien webhook** du salon Discord est à coller dans Paramètres (envoie-le en privé).

## Le brief complet pour eux

→ [BRIEF-TEAM_envoi-auto-discord.md](BRIEF-TEAM_envoi-auto-discord.md) (mode d'emploi détaillé)

---

## Pour régénérer l'installeur (toi, Pascal)

Si tu modifies le toolkit et veux un nouvel installeur :

1. Recompiler : `cargo build --release --bin wows_toolkit` (dans `C:\Games\WoWs-Toolkit`)
2. Recompiler l'installeur (il ré-embarque l'exe à jour) :
`cargo build --release` dans le projet `installer-rs`
3. Récupérer le `.exe` produit et le renvoyer aux potes

*(Le code source de l'installeur est dans le scratchpad de la session ; demande à Claude
de le replacer dans le repo si tu veux le garder en permanence.)*
16 changes: 16 additions & 0 deletions _INSTALLEUR-TEAM/installer-src/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Projet autonome (hors workspace principal) : installeur des potes.
# Le [workspace] vide ci-dessous le declare independant du Cargo.toml racine du repo.
[workspace]

[package]
name = "installateur-toolkit-team"
version = "1.0.0"
edition = "2021"

[[bin]]
name = "Installateur-WoWsToolkit-Team"
path = "src/main.rs"

[profile.release]
opt-level = 2
strip = true
82 changes: 82 additions & 0 deletions _INSTALLEUR-TEAM/installer-src/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//! Installateur autonome WoWs Toolkit (version team : webhook + filtre Clan Wars).
//! Embarque l'exe modifie et l'installe au lancement (espace utilisateur, sans UAC).

use std::io::Write;
use std::path::PathBuf;
use std::process::Command;

/// L'exe modifie est embarque dans cet installeur a la compilation.
const TOOLKIT: &[u8] = include_bytes!(r"C:\Games\WoWs-Toolkit\target\release\wows_toolkit.exe");

fn main() {
println!(" ====================================================");
println!(" Installation de WoWs Toolkit - version team");
println!(" (envoi auto Discord + filtre Clan Wars)");
println!(" ====================================================\n");

// Dossier de destination : %LOCALAPPDATA%\WoWs Toolkit
let local = std::env::var("LOCALAPPDATA").unwrap_or_default();
if local.is_empty() {
eprintln!(" [ERREUR] Variable LOCALAPPDATA introuvable.");
pause();
std::process::exit(1);
}
let dest_dir = PathBuf::from(&local).join("WoWs Toolkit");
let dest_exe = dest_dir.join("wows_toolkit.exe");

// Fermer l'app si elle tourne (sinon fichier verrouille)
let _ = Command::new("taskkill")
.args(["/IM", "wows_toolkit.exe", "/F"])
.output();
std::thread::sleep(std::time::Duration::from_millis(1200));

// Creer le dossier + ecrire l'exe
if let Err(e) = std::fs::create_dir_all(&dest_dir) {
eprintln!(" [ERREUR] Creation du dossier impossible : {e}");
pause();
std::process::exit(1);
}
if let Err(e) = std::fs::write(&dest_exe, TOOLKIT) {
eprintln!(" [ERREUR] Ecriture de l'application impossible : {e}");
eprintln!(" Ferme WoWs Toolkit s'il est ouvert puis relance l'installeur.");
pause();
std::process::exit(1);
}
println!(" [OK] Application installee dans :");
println!(" {}", dest_dir.display());

// Raccourci sur le bureau via PowerShell (WScript.Shell)
let ps = format!(
"$w=New-Object -ComObject WScript.Shell; \
$sc=$w.CreateShortcut([Environment]::GetFolderPath('Desktop')+'\\WoWs Toolkit (team).lnk'); \
$sc.TargetPath='{}'; $sc.WorkingDirectory='{}'; $sc.Save()",
dest_exe.display(),
dest_dir.display()
);
let shortcut_ok = Command::new("powershell")
.args(["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", &ps])
.output()
.map(|o| o.status.success())
.unwrap_or(false);
if shortcut_ok {
println!(" [OK] Raccourci cree sur le bureau : \"WoWs Toolkit (team)\"");
} else {
println!(" [i] Raccourci non cree (lance l'exe depuis le dossier ci-dessus).");
}

println!("\n ----------------------------------------------------");
println!(" Termine ! Lance le jeu via le raccourci du bureau.");
println!(" IMPORTANT : si l'appli propose une mise a jour,");
println!(" REFUSE-LA (sinon la version team est ecrasee).");
println!(" ----------------------------------------------------\n");

pause();
}

/// Attend l'appui sur Entree (utile quand lance par double-clic).
fn pause() {
print!(" Appuie sur Entree pour fermer...");
let _ = std::io::stdout().flush();
let mut s = String::new();
let _ = std::io::stdin().read_line(&mut s);
}
68 changes: 68 additions & 0 deletions _PARTAGE-TEAM-DISCORD/BRIEF-TEAM_envoi-auto-discord.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# 📤 WoWs Toolkit (version team) — Envoi auto Discord

Salut la team 👋
Voici le mode d'emploi de la version modifiée du **WoWs Toolkit** que je vous ai partagée.
En gros : **en fin de partie, l'appli poste automatiquement sur notre Discord** la vidéo
du replay (accélérée) + le tableau de stats de l'équipe. Plus besoin de rien faire à la main.

---

## ⚙️ Réglage le plus important : quelles parties sont envoyées

Par **défaut, SEULES les parties Clan Wars sont envoyées.** Vos randoms / co-op / brawls
ne partent **pas** sur le Discord — tranquille pour jouer en solo sans spammer le salon.

### Pour changer ça (2 endroits)

**1. Le plus rapide — le menu en haut de la fenêtre :**
En haut, à côté du bouton *Discord*, il y a un menu qui affiche l'état en clair :

- **📤 Discord : OFF** → rien n'est envoyé
- **📤 Discord : Clan Wars** → seules les Clan Wars partent *(réglage par défaut)*
- **📤 Discord : Toutes parties** → toutes vos parties partent

Cliquez dessus → vous pouvez :
- cocher/décocher **« Envoi automatique »** (le coupe complètement)
- choisir **« Clan Wars uniquement »** ou **« Toutes les parties »**

**2. Sinon, onglet *Paramètres* → section *Envoi automatique Discord*** : mêmes options
(case « Poster automatiquement » + choix Clan Wars / Toutes les parties + le lien webhook).

---

## 🔌 Mise en place (à faire une fois)

1. Ouvrir l'onglet **Paramètres → Envoi automatique Discord**
2. Coller le **lien webhook** du salon Discord dans *« Lien webhook Discord »*
*(format `https://discord.com/api/webhooks/...` — je vous le file en privé)*
3. Choisir le **dossier des vidéos** (où elles sont enregistrées avant envoi)
4. Régler la **vitesse vidéo** (x20 = vidéo courte, x5 = plus longue)
5. Vérifier que **« Envoi automatique »** est coché

> 💡 La section affiche la taille du dossier vidéos et propose un bouton **« Vider le dossier »**
> dès que ça dépasse 1 Go — pensez-y de temps en temps.

---

## 🚫 IMPORTANT — refuser la mise à jour officielle

L'appli officielle se met à jour toute seule et **écrase notre version modifiée**
(l'envoi Discord disparaît alors). Donc :

- Au démarrage, si une mise à jour est proposée → **refusez-la**.
- Dans **Paramètres**, **décochez « Vérifier les mises à jour au démarrage »**.
- Si jamais vous perdez la fonction → c'est que la maj est passée, redemandez-moi le fichier.

---

## ❓ Récap express

| Je veux… | Je fais… |
|---|---|
| Ne rien envoyer (jouer peinard) | Menu haut → **📤 Discord : OFF** |
| Envoyer seulement les Clan Wars | **📤 Discord : Clan Wars** *(défaut)* |
| Tout envoyer | **📤 Discord : Toutes parties** |
| Mettre en place le salon | Paramètres → coller le **webhook** |
| Garder la modif | **Refuser** les mises à jour officielles |

Des questions → pingez-moi sur le Discord. GG 🚢
10 changes: 10 additions & 0 deletions crates/minimap-renderer/src/draw_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ pub enum ShipConfigCircleKind {
Hydro,
}

/// Detection consumable a pulsing radius represents, used to flag it the instant it lights up.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
pub enum DetectionPulse {
Radar,
Hydro,
}

/// Per-range-type visibility filter for ship configuration circles.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
Expand Down Expand Up @@ -473,6 +481,8 @@ pub enum DrawCommand {
color: [u8; 3],
/// Fill transparency
alpha: f32,
/// When set, pulse the circle to flag a just-activated detection consumable
pulse: Option<DetectionPulse>,
},
/// Fighter patrol radius circle (filled only, no outline)
PatrolRadius {
Expand Down
8 changes: 3 additions & 5 deletions crates/minimap-renderer/src/encoder/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,15 @@ impl CpuEncoder {
.bitrate(BitRate::from_bps(bitrate))
.complexity(Complexity::High);
let encoder = Encoder::with_api_config(OpenH264API::from_source(), config)
.map_err(|e| report!(VideoError::EncoderInit(format!("Failed to create H.264 encoder: {e:?}"))))?;
.context(VideoError::EncoderInit)
.attach("creating openh264 H.264 encoder")?;
Ok(Self { encoder })
}

pub fn encode_frame(&mut self, rgb: &[u8], width: usize, height: usize) -> rootcause::Result<Vec<u8>, VideoError> {
let rgb_slice = RgbSliceU8::new(rgb, (width, height));
let yuv = YUVBuffer::from_rgb_source(rgb_slice);
let bitstream = self
.encoder
.encode(&yuv)
.map_err(|e| report!(VideoError::EncodeFailed(format!("H.264 encode error: {e:?}"))))?;
let bitstream = self.encoder.encode(&yuv).context(VideoError::EncodeFailed).attach("openh264 encode")?;
Ok(bitstream.to_vec())
}
}
17 changes: 8 additions & 9 deletions crates/minimap-renderer/src/encoder/cpu_av1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ pub struct CpuAv1Encoder {
impl CpuAv1Encoder {
pub fn new(width: u32, height: u32, config: Cfg) -> rootcause::Result<Self, VideoError> {
if !width.is_multiple_of(2) || !height.is_multiple_of(2) {
return Err(report!(VideoError::EncoderInit(format!(
"AV1 requires even dimensions; got {width}x{height}"
))));
return Err(
report!(VideoError::EncoderInit).attach(format!("AV1 requires even dimensions; got {width}x{height}"))
);
}

// Preset 8 prioritizes encode speed (~2x faster than preset 6); the cost
Expand Down Expand Up @@ -87,7 +87,7 @@ impl CpuAv1Encoder {

let config = Config::new().with_encoder_config(cfg);
let ctx: Context<u8> =
config.new_context().map_err(|e| report!(VideoError::EncoderInit(format!("rav1e context: {e:?}"))))?;
config.new_context().context(VideoError::EncoderInit).attach("creating rav1e context")?;

let y_size = (width * height) as usize;
let chroma_size = ((width / 2) * (height / 2)) as usize;
Expand Down Expand Up @@ -116,9 +116,7 @@ impl CpuAv1Encoder {
frame.planes[1].copy_from_raw_u8(&self.u_buf, chroma_w, 1);
frame.planes[2].copy_from_raw_u8(&self.v_buf, chroma_w, 1);

self.ctx
.send_frame(frame)
.map_err(|e| report!(VideoError::EncodeFailed(format!("rav1e send_frame: {e:?}"))))?;
self.ctx.send_frame(frame).context(VideoError::EncodeFailed).attach("rav1e send_frame")?;
self.frames_sent += 1;

self.drain(false)
Expand All @@ -143,7 +141,7 @@ impl CpuAv1Encoder {
}
Err(EncoderStatus::LimitReached) => return Ok(out),
Err(other) => {
return Err(report!(VideoError::EncodeFailed(format!("rav1e receive_packet: {other:?}"))));
return Err(report!(other).context(VideoError::EncodeFailed).attach("rav1e receive_packet"));
}
}
}
Expand Down Expand Up @@ -174,7 +172,8 @@ impl CpuAv1Encoder {
YuvStandardMatrix::Bt709,
YuvConversionMode::Balanced,
)
.map_err(|e| report!(VideoError::EncodeFailed(format!("RGB->I420: {e:?}"))))?;
.context(VideoError::EncodeFailed)
.attach("RGB->I420 conversion")?;
let _ = chroma_h;
Ok(())
}
Expand Down
Loading