0. Résumé exécutif
Ce dossier met en évidence deux sous-systèmes distincts qu’il faut éviter de mélanger :
A. PART SELECTION
Quelle partie ED2K faut-il privilégier ?
B. BLOCK SCHEDULING
Quelles plages faut-il réserver à quelle source et combien faut-il en maintenir en vol ?
L’eMule officiel possède déjà un Enhanced Chunk Selection beaucoup plus riche que le simple « rarest first ».
Dans la branche 0.72a étudiée, le classement d’une partie utilise notamment :
- fréquence / rareté ;
- prévisualisation ;
- état déjà demandé ;
- complétion ;
- continuité avec la partie précédente de la source ;
- nombre de sources actives pouvant fournir la partie ;
- estimation de vitesse de complétion.
En revanche, le scheduler standard protège fortement l’unicité des réservations :
un bloc déjà demandé ne doit normalement pas être redemandé à une autre source.
C’est sain en régime normal mais crée le problème classique d’endgame :
GetUnrequestedSize() == 0
mais
le fichier reste incomplet
car les derniers blocs sont immobilisés
chez une ou plusieurs sources lentes.
Notre ancien audit NetF WARP / Spike2 / Silversurfer apporte déjà les briques historiques nécessaires à une amélioration compatible :
- Dynamic Block Requests ;
- Drop Stalled Sources ;
- Delayed NNP ;
- PartCompletionSpeedUp.
ne pas faire du duplicate endgame agressif ; adapter la granularité et réattribuer sélectivement les réservations qui deviennent contre-productives.
Un troisième axe apparaît avec notre étude anti-leecher :
la rareté est calculée à partir de cartes de parties déclarées par les pairs et n’est donc pas une vérité de confiance.
0.1 Repères directs dans eMule Community 0.72a
Les points principaux de ce dossier ont été recroisés directement dans l’archive source fournie.
Opcodes.h
PARTSIZE ligne ~102
EMBLOCKSIZE ligne ~135
PartFile.cpp
IsAlreadyRequested() ligne ~1662
ShrinkToAvoidAlreadyRequested()
ligne ~1686
GetNextEmptyBlockInPart() ligne ~1765
UpdatePartsInfo() ligne ~2517
GetNextRequestedBlock() ligne ~4551
DownloadClient.cpp
ProcessFileStatus() ligne ~495
CreateBlockRequests() ligne ~767
SendBlockRequests() ligne ~798
La phrase de commentaire la plus importante se trouve au début de GetNextRequestedBlock() :
The selection of the chunk to download is one of the CRITICAL
parts of the edonkey network.
Le code précise que l’objectif est d’assurer la meilleure diffusion des fichiers, pas seulement de minimiser l’ETA du client local.
1. Contraintes protocolaires de base
1.1 Taille d’une partie ED2K
eMule 0.50a et 0.72a :
PARTSIZE = 9728000Soit :
9 728 000 octets
≈ 9,277 MiB
Cette valeur appartient à l’identité ED2K du fichier.
La changer casserait :
- le hash ED2K ;
- les hashsets ;
- les cartes de parties ;
- l’interopérabilité avec les fichiers existants.
2. Taille de bloc historique eMule
0.72a :
EMBLOCKSIZE = 184320Soit :
180 KiB
Il s’agit d’une taille maximale logique de demande courante, pas d’une obligation d’envoyer exactement 180 KiB à chaque requête.
GetNextEmptyBlockInPart() limite un bloc à cette frontière mais peut retourner une plage plus courte pour combler précisément un gap.
Une partie pleine correspond donc à :
52 blocs de 180 KiB
+ un dernier bloc de 143 360 octets
si on la parcourt depuis son début avec cette granularité.
3. Le format de requête historique
edonkey-protocol.txt :
<Request parts>
<File hash>
<Start offset> × 3
<End offset> × 3
eMule 0.72a conserve cette structure legacy.
Dans DownloadClient.cpp, SendBlockRequests() écrit :
aOffs[0..2] = starts
aOffs[3..5] = endsavec :
OP_REQUESTPARTSou :
OP_REQUESTPARTS_I64pour les offsets de grands fichiers.
Conséquence
Le format historique transporte au plus :
3 ranges
dans un paquet de requête classique.
Mais le client peut conserver localement davantage de blocs pending et envoyer plusieurs paquets successifs.
4. Une optimisation ancienne qu’il ne faut pas perdre
Commentaire Barry dans DownloadClient.cpp / PartFile.cpp :
Historiquement, le code attendait autrefois d’avoir reçu un bloc complet de 180 KiB et demandait les données par blocs rigides.
Conséquence moyenne signalée dans le commentaire :
≈ 90 KiB perdus par rupture de connexion
La logique actuelle :
- écrit les paquets reçus dans des buffers ;
- remplit immédiatement les gaps ;
- demande seulement les plages manquantes ;
- limite une requête à 180 KiB mais autorise des plages plus courtes.
5. Réservation locale des blocs
GetNextEmptyBlockInPart() :
- cherche un gap ;
- borne la plage à la partie ;
- borne la plage à
EMBLOCKSIZE; - vérifie
IsAlreadyRequested(); - si collision, appelle
ShrinkToAvoidAlreadyRequested(); - ne retourne que la portion non réservée.
Extrait logique :
if (!IsAlreadyRequested(start, end, true))
return block;
if (ShrinkToAvoidAlreadyRequested(tempStart, tempEnd))
return shrunken_block;Conclusion
La politique officielle 0.72a est :
une plage déjà réservée ne doit pas être réservée à nouveau si une plage libre peut être trouvée.
Il n’existe pas ici de duplicate endgame général.
6. requestedblocks_list
Chaque bloc choisi est enregistré dans :
requestedblocks_listavant d’être renvoyé au client demandeur.
Ce registre sert notamment à :
- éviter les doubles réservations ;
- calculer le niveau de complétion « en vol » d’une partie ;
- retirer les réservations lorsqu’un client abandonne ;
- réouvrir les plages si nécessaire.
Les blocs déjà demandés peuvent même être comptés comme pratiquement téléchargés dans le calcul relatif de complétion d’une partie.
Le commentaire précise que cela peut faire dépasser temporairement PARTSIZE dans ce calcul comparatif ; la valeur est ensuite plafonnée.
7. Source des informations de rareté
CPartFile::UpdatePartsInfo() remet d’abord :
m_SrcPartFrequency[]à zéro.
Puis pour chaque source possédant une PartStatus :
m_SrcPartFrequency[i] += cur_src->IsPartAvailable(i);Donc :
frequency(part i)
=
nombre de sources connues annonçant cette partie
Ce compteur alimente directement GetNextRequestedBlock().
8. Important : la carte de parties est déclarative
CUpDownClient::ProcessFileStatus() :
- vérifie que le nombre de parties reçu correspond au fichier ;
- lit chaque bit de la carte ;
- remplit
m_abyPartStatus[].
Le code vérifie la cohérence structurelle :
nombre de parties correct
taille de message correcte
fichier correct
Il ne possède évidemment pas une preuve instantanée que le pair possède réellement tous les octets correspondant aux bits à 1.
Conséquence Trust
Un pair hostile peut annoncer une carte de parties fausse.
Effets théoriques :
- être candidat pour une partie qu’il ne peut pas réellement transmettre ;
- influencer
m_SrcPartFrequency; - faire paraître une partie rare plus commune qu’elle ne l’est réellement ;
- provoquer des décisions de sélection sous-optimales.
Ce qu’il ne faut pas conclure
Une variation de carte n’est pas automatiquement une attaque.
Un pair est lui-même en téléchargement : sa carte peut légitimement évoluer.
10. Enhanced Chunk Selection — origine
eMule 0.50a porte explicitement :
[Maella -Enhanced Chunk Selection- (based on jicxicmic)]
La branche 0.72a conserve l’architecture et l’essentiel des coefficients, avec plusieurs raffinements.
aMule conserve également une branche dérivée de cette même famille de sélection, avec une formule différente.
Cela confirme :
- le concept est largement partagé ;
- les coefficients précis appartiennent à la politique client.
11. Commentaire clé des sources
La 0.72a décrit GetNextRequestedBlock() comme :
one of the CRITICAL parts of the edonkey network
Objectif déclaré :
ensure the best spreading of files
Ce point est crucial.
Le scheduler n’est pas conçu uniquement pour réduire notre ETA.
Il cherche aussi à améliorer la diffusion des parties dans le swarm eD2K.
ETA locale
+
santé / diffusion du fichier
Une optimisation purement égoïste de vitesse pourrait réduire la robustesse du réseau.
13. Seuils de rareté eMule 0.72a
limit = max((GetSourceCount() + 9) / 10, 3);
veryRareBound = limit;
rareBound = 2 * limit;
almostRareBound = 4 * limit;Approximation :
very rare ≲ 10 % des sources, min 3
rare ≲ 20 %
almost rare ≲ 40 %
common au-delà
Les seuils restent exprimés en nombre brut, mais suivent le nombre de sources.
14. Critère 1 — fréquence
Pour chaque partie candidate :
frequency = m_SrcPartFrequency[part]Plus la valeur est faible, plus la partie est rare.
Le poids de cette fréquence est très fort dans les zones rare / very rare.
Exemple :
very rare:
rank += 75 * frequency
rare:
rank += 25 * frequencyRang plus petit = meilleur.
15. Critère 2 — preview
Si la fonction de priorité preview est active et que le type de fichier est prévisualisable, eMule favorise :
première partie
dernière partie
avant-dernière partie si la dernière est très petite
Dans la 0.72a, la fonction n’est active que pour un fichier de taille > 2 * PARTSIZE.
16. Critère 3 — déjà demandé
Le code détermine si des blocs de cette partie sont déjà en cours auprès d’autres sources.
Ce critère n’est pas simplement :
jamais deux sources sur la même partie
Il est pondéré différemment suivant la zone de rareté.
Raison
Pour une partie très rare :
répliquer vite
peut justifier plusieurs sources travaillant sur différentes plages de la même partie.
Pour les parties communes, eMule cherche plutôt à terminer intelligemment ce qui est en cours.
17. Critère 4 — complétion
La complétion est calculée en partant de :
taille de la partie
-
gaps restant
+
blocs déjà réservés
Le dernier chunk est volontairement traité comme s’il faisait la taille complète afin de ne pas recevoir un avantage artificiel simplement parce qu’il est plus petit.
Priority inversion
Une partie proche de 100 % peut dépasser une partie légèrement plus rare.
Ce comportement est volontaire.
18. Critère 5 — même partie que précédemment
sameChunk =
cur_chunk.part == sender->m_lastPartAskedPetit bonus pour continuer la partie avec laquelle cette source travaillait déjà.
Intérêt
- réduit les bascules ;
- améliore la continuité ;
- favorise la terminaison des parties ;
- diminue les réservations dispersées.
19. Critère 6 — sources actives capables de servir la partie
Le code part du nombre de sources actuellement en téléchargement puis décrémente pour celles qui annoncent cette partie.
Il utilise ainsi une information du type :
combien de clients actifs pourraient contribuer à finir cette partie
Dans les zones très rares / rares, elle influence le classement.
20. Critère 7 — estimation de fin de partie
La 0.72a calcule :
totalDownloadDatarateForThisPartà partir des clients actifs possédant la partie.
Puis :
bandwidthScore =
min(
(PARTSIZE - partSize)
/
(totalDownloadDatarateForThisPart * 5),
2000
);Ce score approxime donc l’effort restant rapporté au débit disponible.
C’est déjà une tentative d’ETA locale par partie.
21. Les rangs par zone
Schéma simplifié 0.72a :
Beaucoup d’A4AF
Priorité très forte à la complétion.
Très rare
Base autour de :
3000 + 75 × frequency
puis complétion / request state / même chunk / sources actives.
Preview
Zone :
≈ 10000 ou 20000
Rare
Zone :
≈ 10101 + 25 × frequency
Almost rare
Zone :
≈ 20101+
avec hasard modéré et poids plus fort de complétion.
Common
Zone :
≈ 30000+
où la complétion et l’estimation de bande passante deviennent importantes.
Le rang le plus bas gagne.
22. Aléatoire en cas d’égalité
Une fois le meilleur rang trouvé, eMule sélectionne aléatoirement une des parties exactement ex æquo.
Commentaire :
avoid that everybody tries to download
the same chunks at the same time
C’est une bonne protection contre les décisions synchronisées.
Note
Le code utilise rand() ici.
Contrairement au SecureIdent du dossier 03, ce hasard n’a pas besoin d’être cryptographiquement sûr.
Mais un générateur moderne local, thread-safe et testable pourrait rendre la logique plus propre.
Priorité faible.
23. Continuité 0.50a → 0.72a
Le cœur du Enhanced Chunk Selection est déjà présent en 0.50a.
On retrouve :
- Maella / jicxicmic ;
- rareté ;
- preview ;
- requested state ;
- completion ;
- same chunk ;
- nombre de sources actives ;
- bandwidth/completion score ;
- random tie-break.
La 0.72a conserve donc un algorithme dont la philosophie a au moins quinze ans.
Cela ne veut pas dire qu’il est mauvais.
Au contraire, il est assez sophistiqué.
Mais c’est un candidat idéal pour :
instrumentation
+
simulation
+
comparaison avec des critères modernes
avant toute retouche.
24. Comparaison avec l’archive aMule fournie
L’arbre aMule fourni contient lui aussi :
[Maella -Enhanced Chunk Selection- (based on jicxicmic)]
mais sa formule est différente :
- zones rareté différentes ;
- moins de critères dans la version observée ;
- traitement différent des parties déjà demandées ;
- choix aléatoire parmi les ex æquo.
Cette divergence confirme encore :
le wire protocol impose la carte des parties et les ranges ; le ranking exact des parties est une politique locale.
25. Scheduling des blocs dans eMule 0.50a
SendBlockRequests() :
blockCount = 3;mais quand il reste peu de données :
≤ 4 parties restantes
et que la source est lente :
< ~600 B/s / faible payload → 1 bloc
< ~1200 B/s → 2 blocs
sinon → 3 blocs
Commentaire source :
prevent locking of too many blocks
when we are on a slow slot
Point historique important
Le problème endgame était donc déjà identifié dans l’officiel sous une forme partielle.
26. Scheduling des blocs dans eMule 0.72a
La 0.72a généralise l’adaptation :
if eMule source && rate < 9 KiB/s:
< 4 KiB/s -> 1
sinon -> 2
else if rate > 75 KiB/s:
>150 KiB/s -> 9
sinon -> 6
else:
3Signification
Le nombre de blocs préparés devient une forme de fenêtre de requêtes :
source lente → petite fenêtre
source rapide → fenêtre plus profonde
C’est une évolution importante pour les connexions rapides.
27. Mais le packet legacy reste à trois ranges
SendBlockRequests() prend jusqu’à trois blocs non encore envoyés et les encode dans le paquet.
Les blocs supplémentaires restent localement pending.
Donc :
local pending window ≠ nombre de ranges par packet
On peut optimiser la profondeur locale sans changer le format de paquet.
28. Problème potentiel de la logique actuelle à haut débit
Avec :
9 blocs pending × 180 KiB
≈ 1,58 MiB réservés à une source
une source rapide les termine très vite.
Ce n’est pas problématique en régime normal.
Mais à proximité de la fin :
- plusieurs blocs peuvent représenter une part significative du reste ;
- la source peut ralentir brusquement ;
- les réservations peuvent immobiliser les dernières données.
La 0.72a ne conditionne plus son 1/2/3/6/9 explicitement au fait d’être dans les quatre dernières parties comme la 0.50a.
30. État NORMAL
Critères possibles :
beaucoup de données non réservées
ETA longue
plusieurs parties disponibles
Comportement :
- Enhanced Chunk Selection actuel ;
EMBLOCKSIZEstandard ;- profondeur pending standard ;
- aucun drop endgame.
31. État APPROACH
Déclenché par exemple lorsque :
ETA devient courte
OU
≤ quelques parties restent
Comportement :
- réduire progressivement la quantité confiée aux sources lentes ;
- instrumenter l’ETA par bloc ;
- conserver le scheduler normal de parties.
32. État ENDGAME
Le fichier possède encore des données libres, mais peu.
Comportement :
Dynamic Block Requests
avec taille de plage dérivée de :
source_rate × target_time
et plafonnée à EMBLOCKSIZE dans une première version prudente.
Objectif :
chaque réservation doit pouvoir finir
bien avant l’ETA globale du fichier
33. État LOCKED
Définition conceptuelle :
unrequested_bytes == 0
ET
file incomplete
Tous les octets manquants sont donc déjà réservés.
C’est la situation où le scheduler standard peut devenir prisonnier d’une source lente.
34. Leçons du rapport NetF WARP / Spike2 / Silversurfer
Notre audit antérieur a déjà établi :
Dynamic Block Requests
Adapte la taille des demandes à :
- vitesse source ;
- vitesse fichier ;
- ETA globale.
NetF utilisait des valeurs historiques :
min ≈ 12 KiB
alignement 4 KiB
fenêtre ≤ 90 s
et pouvait aller jusqu’à plusieurs EMBLOCKSIZE.
Drop Stalled Sources
Si aucun bloc n’est libre :
- analyser les réservations ;
- repérer une source bloquée/lente ;
- annuler proprement son transfert ;
- libérer ses blocs ;
- permettre à une meilleure source de les reprendre.
Delayed NNP
Conserver brièvement une source qui semble No Needed Parts avant de la jeter, surtout en fin de fichier.
PartCompletionSpeedUp
Réduire la granularité près de la fin d’une partie.
35. Signal historique Spike2
Spike2 a intégré :
Dazzle’s Faster Endgame
puis l’a retiré.
Le changelog indique explicitement :
REMOVED: Dazzle's Faster Endgame
(due to implementation of netfinity's Dynamic Block Requests)
36. Pourquoi ne pas faire un duplicate-request endgame simple ?
Approche naïve :
dernier bloc déjà demandé à A
→ demander le même à B
→ garder le premier reçu
Avantage :
latence potentiellement minimale
Inconvénients :
- octets dupliqués ;
- gaspillage de bande passante upload des pairs ;
- annulations tardives ;
- corruption statistique des débits ;
- complexité avec compression ;
- davantage de contrôle réseau ;
- risque de comportements agressifs.
Dans un réseau coopératif ancien comme eD2K, ce coût est difficile à justifier en premier choix.
37. Amélioration par rapport au Drop Stalled NetF
L’audit historique a repéré une limite :
NetF peut sélectionner une source stalled à abandonner sans vérifier explicitement que la nouvelle source en attente possède réellement le bloc qu’on va libérer.
Pseudo-principe :
candidate pending block
∩
parts advertised by waiting source
≠ vide
Sinon :
ne pas dropper
38. ETA de bloc
Pour chaque réservation :
remaining =
(end - start + 1) - transferred
eta =
remaining / effective_rate
Mais effective_rate ne doit pas être seulement le débit instantané.
Proposition à tester :
max(
EWMA court terme,
percentile bas récent,
petit plancher
)
Éviter :
- oscillations brutales ;
- un pic de débit de quelques millisecondes ;
- division par zéro.
39. Définir « stalled »
Ne pas reprendre directement les constantes NetF :
5 s
10 s
15 s
Elles sont historiquement intéressantes mais arbitraires en 2026.
Utiliser des conditions combinées :
aucun octet depuis X RTT / X secondes
socket sans progression
ETA bloc très supérieure à meilleure alternative
source alternative prête
pas de pression disque locale expliquant le retard
Très important pour DiskGuard :
une source ne doit pas être déclarée stalled parce que notre propre disque bloque le pipeline.
42. Liaison avec le Dossier 05 — Trust / Anti-Leecher
Le Dossier 05 a formalisé la distinction REMOTE_CLAIM / LOCAL_MEASURED / CRYPTO_OR_HASH_VERIFIED.
Pour le scheduler de téléchargement :
Part Status reçu = REMOTE_CLAIM
bloc effectivement reçu = LOCAL_MEASURED
partie validée par hash = HASH_VERIFIED
43. Security / Trust : impact sur Endgame
Un pair malveillant peut mentir sur sa part map.
Un pair peut aussi :
- annoncer une capacité ;
- entrer en queue ;
- servir lentement ou jamais ;
- changer sa carte.
Endgame est donc un endroit où un comportement hostile peut coûter cher : les derniers blocs ont une grande valeur marginale.
43. Ne pas confondre Trust et ban
Le futur score de confiance ne doit pas devenir :
une petite anomalie = ban
Utilisations possibles au départ :
télémétrie
tie-break
priorité de reallocation endgame
choix entre deux sources équivalentes
Pas :
blacklist automatique permanente
sans preuves fortes.
46. Première version : observation uniquement
Avant toute modification, loguer pour chaque fichier :
[PART_SELECT]
part=17
frequency=2
zone=VERY_RARE
completion=41
already_requested=1
same_source_chunk=0
active_capable_sources=2
bandwidth_score=183
rank=3245
[BLOCK_RESERVE]
source=...
start=...
end=...
size=184320
pending_for_source=3
[ENDGAME_STATE]
state=APPROACH
remaining=18.2MiB
unrequested=8.4MiB
pending=9.8MiB
eta=54s
47. Détecter le vrai « locked endgame »
Trace :
[ENDGAME_LOCKED]
remaining=721KiB
unrequested=0
pending_blocks=5
active_sources=4
alternate_sources=3
Puis détailler :
block A
holder rate=42 KiB/s
ETA=3s
alternative=none
block B
holder rate=0.9 KiB/s
ETA=124s
alternative rate=220 KiB/s
alternative has part=yes
C’est seulement dans le second cas qu’une réallocation devient évidente.
48. Pseudocode de décision de réallocation
if state != LOCKED:
do nothing
for each pending block:
current_holder = holder(block)
alternatives = sources_that_can_serve(block)
if alternatives.empty:
continue
current_eta = estimate(current_holder, block)
best_alt_eta = best_estimate(alternatives, block)
if current_holder is truly stalled
OR current_eta >> best_alt_eta:
consider targeted cancel/reassign
Ajouter garde-fous :
minimum time since reservation
minimum confidence in alternate source
no local disk pressure
cooldown after reallocation
only one rescue at a time initially
51. Rareté et ETA : objectifs parfois opposés
Exemple :
Partie A
1 seule source
0 % téléchargé
source à 15 KiB/s
Partie B
5 sources
98 % téléchargé
sources à 2 MiB/s
Le scheduler actuel possède déjà des mécanismes de priority inversion.
toujours ETA minimale
ni par :
toujours fréquence minimale
Il doit explicitement arbitrer :
résilience du swarm
versus
temps de complétion local
52. Métriques à collecter
Par fichier :
part selections
zone rarity distribution
part completion latency
number of simultaneous parts
blocks reserved
block reassignment candidates
actual reassignments
wasted bytes
endgame duration
time spent locked
Par source :
requested bytes
received bytes
mean block completion time
timeout count
part-map claim changes
successful part claims
53. Métrique clé : Endgame Tax
Définition proposée :
Endgame Tax =
temps entre
« 95 % de l’ETA théorique atteinte »
et
« fichier réellement terminé »
Ou plus mesurable :
temps réel des derniers N MiB
/
temps attendu au débit moyen précédent
Exemple :
10 MiB restants
débit précédent : 2 MiB/s
théorique : 5 s
réel : 85 s
Endgame factor = 17×
Cette métrique permet de démontrer si Endgame Manager apporte réellement quelque chose.
54. Métrique clé : Reservation Efficiency
bytes_reserved
bytes_delivered
bytes_cancelled
bytes_reassigned
bytes_duplicate
Objectif :
max(delivered)
min(cancelled + duplicate)
55. Simulation avant activation
« j’aurais réalloué ce bloc »
sans le faire.
Log :
[ENDGAME_SHADOW]
block=...
current_eta=92s
alternate_eta=4s
decision=WOULD_REASSIGN
actual_result=current completed in 71s
Après suffisamment d’échantillons :
simulation gain estimé
faux positifs
réallocations qui auraient été inutiles
Cette approche est bien plus sûre qu’un portage immédiat d’un Mod de 2007.
57. Changements sûrs à court terme
C. Calcul shadow de l’état Endgame
Aucune réallocation réelle.
D. Distinguer dans les stats
announced availability
effective availability
sans influencer encore le choix.
58. Changements à réserver à une phase expérimentale
- request size dynamique ;
- réduction contextuelle de pending depth ;
- targeted reallocation ;
- NNP grace ;
- tie-break fondé partiellement sur l’evidence source.
Tous compatibles en principe avec le wire protocol historique, mais à tester avec :
eMule
aMule
anciens clients
compressed blocks
large files
A4AF
AICH
obfuscation
59. Ce qu’il ne faut pas changer
PARTSIZE
hash ED2K
format des part maps
sémantique OP_REQUESTPARTS
structure des ranges legacy
sans extension négociée.
Aucune nécessité n’apparaît actuellement de modifier ces éléments pour obtenir un meilleur endgame.
60. Ce qu’il ne faut pas porter aveuglément depuis les Mods
- constantes de vitesse anciennes ;
- timers 5/10/15/30 s sans mesure ;
- code de
SwitchBlocks()historique ; - Dazzle DFE ;
- drop d’une source sans vérifier qu’une alternative possède le bloc ;
- comportement agressif de demandes dupliquées.
Conserver les idées, pas les constantes.
61. Première roadmap d’implémentation
Étape 1 — PartSelection Trace
Enregistrer les critères et rangs.
Étape 2 — Pending Block Trace
Enregistrer propriétaire, taille, âge, progression, ETA.
Étape 3 — Endgame Shadow State
NORMAL / APPROACH / ENDGAME / LOCKED / NNP_GRACE.
Étape 4 — Shadow Reallocator
Décision simulée, aucune action.
Étape 5 — Dynamic Request Size
Première modification réelle, plafonnée à EMBLOCKSIZE.
Étape 6 — Targeted Reallocation
Une seule réallocation simultanée, garde-fous stricts.
Étape 7 — NNP Grace
Seulement si les mesures montrent un gain.
62. Conclusion
L’étude du code officiel montre qu’eMule possède déjà un scheduler de parties sophistiqué, historiquement construit pour préserver la diffusion du fichier dans le réseau.
Le problème principal n’est pas :
« eMule ne sait pas choisir les parties »
mais plutôt :
« la réservation de blocs devient trop rigide
quand il ne reste presque plus rien à télécharger »
C’est exactement le problème que les Mods NetF, Spike2 et Silversurfer ont tenté de traiter.
C’est une couche locale qui :
mesure
prédit
réduit la granularité
et réalloue uniquement lorsqu’il existe
une alternative réellement meilleure
tout en conservant le comportement standard hors endgame.