From b91aa2af04ae88ef1d1609648a845ad794ba81ec Mon Sep 17 00:00:00 2001 From: Halizeur Date: Sun, 10 May 2026 12:42:23 +0200 Subject: [PATCH 1/3] Log Overlay: load whitelist keywords from i18n Switches the hardcoded EN/FR whitelist to an i18n-driven one so other locales can override it via their strings_.properties. - LogOverlay.java loads halizeur.log_overlay.whitelist via I18nAPI - Falls back to a default English whitelist when the key is missing - Adds the key in strings_en.properties and strings_fr.properties Suggested by @do-gamer in the post-merge review of #159. --- .../halizeur/log_overlay/LogOverlay.java | 64 +++++++++++-------- .../dev/shared/lang/strings_en.properties | 1 + .../dev/shared/lang/strings_fr.properties | 1 + 3 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/main/java/dev/shared/halizeur/log_overlay/LogOverlay.java b/src/main/java/dev/shared/halizeur/log_overlay/LogOverlay.java index c086e672..ae34e151 100644 --- a/src/main/java/dev/shared/halizeur/log_overlay/LogOverlay.java +++ b/src/main/java/dev/shared/halizeur/log_overlay/LogOverlay.java @@ -18,6 +18,7 @@ import eu.darkbot.api.extensions.MapGraphics; import eu.darkbot.api.managers.EventBrokerAPI; import eu.darkbot.api.managers.GameLogAPI; +import eu.darkbot.api.managers.I18nAPI; /** * Renders the latest in-game DarkOrbit log messages as an overlay on the @@ -39,36 +40,45 @@ public class LogOverlay implements Behavior, Drawable, Listener, Configurable.properties} file. */ - private static final String[] WHITELIST = { - // Gains (FR) - "gagn", "obtenu", "récup", "recup", "récompense", "recompense", - "collect", "ramass", - // Gains (EN) - "gained", "received", "reward", "earned", - // Currencies / resources - "uridium", "credit", "crédit", "honor", "honneur", - "experience", "expérience", "xp ", - "prometium", "endurium", "terbium", "prometid", "duranium", - "promerium", "seprom", "xenomit", "palladium", - // Boosters / drops - "drop", "booster", - // Errors (FR) - "impossible", "erreur", "échec", "echec", "refusé", "refuse", - "plein", "indisponible", "non disponible", "interdit", - // Errors (EN) - "error", "failed", "refused", "denied", "unavailable", "full", - "cannot", "can't" - }; + private static final String DEFAULT_WHITELIST = + "gained,received,reward,earned," + + "uridium,credit,honor,honour,experience,xp," + + "prometium,endurium,terbium,prometid,duranium," + + "promerium,seprom,xenomit,palladium,drop,booster," + + "error,failed,refused,denied,unavailable,full," + + "cannot,impossible"; private final Deque entries = new ArrayDeque<>(); + private final List whitelist; private LogOverlayConfig config; public LogOverlay(PluginAPI api) { api.requireAPI(EventBrokerAPI.class).registerListener(this); + I18nAPI i18n = api.requireAPI(I18nAPI.class); + this.whitelist = parseKeywords(i18n.getOrDefault( + "halizeur.log_overlay.whitelist", DEFAULT_WHITELIST)); + } + + /** + * Parses a comma-separated list of keywords, trimming whitespace and + * lower-casing each entry. Empty entries are dropped. + */ + private static List parseKeywords(String csv) { + List out = new ArrayList<>(); + if (csv == null) return out; + for (String s : csv.split(",")) { + String t = s.trim().toLowerCase(); + if (!t.isEmpty()) out.add(t); + } + return out; } @Override @@ -100,12 +110,14 @@ public void onLogMessage(GameLogAPI.LogMessageEvent event) { } /** - * Whitelist filter: only display the message if it matches a keyword - * from {@link #WHITELIST} (case-insensitive). + * Whitelist filter: only display the message if it contains at least + * one keyword from {@link #whitelist} (case-insensitive). The list + * comes from the i18n key {@code halizeur.log_overlay.whitelist} and + * thus adapts to the user's selected DarkBot locale. */ private boolean isAllowed(String msg) { String lower = msg.toLowerCase(); - for (String kw : WHITELIST) { + for (String kw : whitelist) { if (lower.contains(kw)) return true; } return false; diff --git a/src/main/resources/dev/shared/lang/strings_en.properties b/src/main/resources/dev/shared/lang/strings_en.properties index f5c2561a..0b7a05cd 100644 --- a/src/main/resources/dev/shared/lang/strings_en.properties +++ b/src/main/resources/dev/shared/lang/strings_en.properties @@ -6,6 +6,7 @@ general.enabled=Enable # ----- Halizeur : Log Overlay ----- halizeur.log_overlay.config=Log Overlay halizeur.log_overlay.enabled=Enabled +halizeur.log_overlay.whitelist=gained,received,reward,earned,uridium,credit,honor,honour,experience,xp,prometium,endurium,terbium,prometid,duranium,promerium,seprom,xenomit,palladium,drop,booster,error,failed,refused,denied,unavailable,full,cannot,impossible do_gamer.simple_healing.hp_repair=HP Repair (Solace, Orcus, Aegis, Hammerclaw) do_gamer.simple_healing.shield_repair=Shield Repair (Aegis, Hammerclaw) diff --git a/src/main/resources/dev/shared/lang/strings_fr.properties b/src/main/resources/dev/shared/lang/strings_fr.properties index 8c63e73f..aa14244c 100644 --- a/src/main/resources/dev/shared/lang/strings_fr.properties +++ b/src/main/resources/dev/shared/lang/strings_fr.properties @@ -6,6 +6,7 @@ general.enabled=Activer # ----- Halizeur : Log Overlay ----- halizeur.log_overlay.config=Log Overlay halizeur.log_overlay.enabled=Activé +halizeur.log_overlay.whitelist=gagn,obtenu,récup,recup,récompense,recompense,collect,ramass,uridium,credit,crédit,honor,honneur,experience,expérience,xp,prometium,endurium,terbium,prometid,duranium,promerium,seprom,xenomit,palladium,drop,booster,erreur,échec,echec,refusé,refuse,plein,indisponible,non disponible,interdit,impossible do_gamer.simple_healing.hp_repair=Réparation des PV (Solace, Orcus, Aegis, Hammerclaw) do_gamer.simple_healing.shield_repair=Réparation du bouclier (Aegis, Hammerclaw) From 5dd2654e7791af46fd5cc0d42f266a457ba592a7 Mon Sep 17 00:00:00 2001 From: Halizeur Date: Sun, 10 May 2026 17:06:25 +0200 Subject: [PATCH 2/3] LogOverlay: switch i18n whitelist to checkbox categories Per do-gamer review: replace the comma-separated halizeur.log_overlay.whitelist i18n key with a Categories sub-config of 6 toggles (gains, currencies, resources, boosters, errors, combat). Each category bundles FR + EN keywords so the filter works on both locales without per-translator upkeep. Default: 5 categories on, combat off. --- .../halizeur/log_overlay/LogOverlay.java | 107 +++++++++++------- .../log_overlay/LogOverlayConfig.java | 29 +++++ .../dev/shared/lang/strings_en.properties | 9 +- .../dev/shared/lang/strings_fr.properties | 9 +- 4 files changed, 111 insertions(+), 43 deletions(-) diff --git a/src/main/java/dev/shared/halizeur/log_overlay/LogOverlay.java b/src/main/java/dev/shared/halizeur/log_overlay/LogOverlay.java index ae34e151..94c9a31a 100644 --- a/src/main/java/dev/shared/halizeur/log_overlay/LogOverlay.java +++ b/src/main/java/dev/shared/halizeur/log_overlay/LogOverlay.java @@ -18,7 +18,6 @@ import eu.darkbot.api.extensions.MapGraphics; import eu.darkbot.api.managers.EventBrokerAPI; import eu.darkbot.api.managers.GameLogAPI; -import eu.darkbot.api.managers.I18nAPI; /** * Renders the latest in-game DarkOrbit log messages as an overlay on the @@ -27,6 +26,11 @@ * Source: {@link GameLogAPI.LogMessageEvent} emitted by DarkBot for each * new system message. Each line disappears after DISPLAY_MS ms so the * canvas does not get cluttered. + * + * Filter: only messages matching at least one keyword from the user's + * checked categories ({@link LogOverlayConfig.Categories}) are kept; + * keywords cover both FR and EN forms so the filter works regardless of + * the active game locale. */ @Feature(name = "Log Overlay", description = "Shows the latest in-game log messages as an overlay on the canvas", @@ -39,46 +43,57 @@ public class LogOverlay implements Behavior, Drawable, Listener, Configurable.properties} file. - */ - private static final String DEFAULT_WHITELIST = - "gained,received,reward,earned," - + "uridium,credit,honor,honour,experience,xp," - + "prometium,endurium,terbium,prometid,duranium," - + "promerium,seprom,xenomit,palladium,drop,booster," - + "error,failed,refused,denied,unavailable,full," - + "cannot,impossible"; + /** Keywords (FR + EN) for "loot / reward" log messages. */ + private static final String[] KW_GAINS = { + // FR + "gagn", "obtenu", "récup", "recup", "récompense", "recompense", + "collect", "ramass", + // EN + "gained", "received", "reward", "earned" + }; + + /** Keywords for in-game currencies (uri, credits, honor, XP). */ + private static final String[] KW_CURRENCIES = { + "uridium", "credit", "crédit", + "honor", "honour", "honneur", + "experience", "expérience", "xp " + }; + + /** Keywords for collectable / refinable resources. */ + private static final String[] KW_RESOURCES = { + "prometium", "endurium", "terbium", "prometid", "duranium", + "promerium", "seprom", "xenomit", "palladium" + }; + + /** Keywords for booster / drop messages. */ + private static final String[] KW_BOOSTERS = { + "drop", "booster" + }; + + /** Keywords (FR + EN) for error / refusal messages. */ + private static final String[] KW_ERRORS = { + // FR + "impossible", "erreur", "échec", "echec", + "refusé", "refuse", "plein", "indisponible", + "non disponible", "interdit", + // EN + "error", "failed", "refused", "denied", + "unavailable", "full", "cannot", "can't" + }; + + /** Keywords (FR + EN) for combat / kill messages. */ + private static final String[] KW_COMBAT = { + // FR + "tué", "tue", "détruit", "detruit", "dégât", "degat", + // EN + "kill", "destroyed", "boss", "damage" + }; private final Deque entries = new ArrayDeque<>(); - private final List whitelist; private LogOverlayConfig config; public LogOverlay(PluginAPI api) { api.requireAPI(EventBrokerAPI.class).registerListener(this); - I18nAPI i18n = api.requireAPI(I18nAPI.class); - this.whitelist = parseKeywords(i18n.getOrDefault( - "halizeur.log_overlay.whitelist", DEFAULT_WHITELIST)); - } - - /** - * Parses a comma-separated list of keywords, trimming whitespace and - * lower-casing each entry. Empty entries are dropped. - */ - private static List parseKeywords(String csv) { - List out = new ArrayList<>(); - if (csv == null) return out; - for (String s : csv.split(",")) { - String t = s.trim().toLowerCase(); - if (!t.isEmpty()) out.add(t); - } - return out; } @Override @@ -110,15 +125,25 @@ public void onLogMessage(GameLogAPI.LogMessageEvent event) { } /** - * Whitelist filter: only display the message if it contains at least - * one keyword from {@link #whitelist} (case-insensitive). The list - * comes from the i18n key {@code halizeur.log_overlay.whitelist} and - * thus adapts to the user's selected DarkBot locale. + * Whitelist filter: only display the message if at least one + * keyword from a checked category appears in it (case-insensitive). */ private boolean isAllowed(String msg) { String lower = msg.toLowerCase(); - for (String kw : whitelist) { - if (lower.contains(kw)) return true; + LogOverlayConfig.Categories c = this.config.categories; + if (c == null) return false; + if (c.gains && containsAny(lower, KW_GAINS)) return true; + if (c.currencies && containsAny(lower, KW_CURRENCIES)) return true; + if (c.resources && containsAny(lower, KW_RESOURCES)) return true; + if (c.boosters && containsAny(lower, KW_BOOSTERS)) return true; + if (c.errors && containsAny(lower, KW_ERRORS)) return true; + if (c.combat && containsAny(lower, KW_COMBAT)) return true; + return false; + } + + private static boolean containsAny(String haystack, String[] needles) { + for (String n : needles) { + if (haystack.contains(n)) return true; } return false; } diff --git a/src/main/java/dev/shared/halizeur/log_overlay/LogOverlayConfig.java b/src/main/java/dev/shared/halizeur/log_overlay/LogOverlayConfig.java index 24c82f36..b5ed9d64 100644 --- a/src/main/java/dev/shared/halizeur/log_overlay/LogOverlayConfig.java +++ b/src/main/java/dev/shared/halizeur/log_overlay/LogOverlayConfig.java @@ -8,4 +8,33 @@ public class LogOverlayConfig { @Option("halizeur.log_overlay.enabled") public boolean enabled = false; + + @Option("halizeur.log_overlay.categories") + public Categories categories = new Categories(); + + /** + * Whitelist categories. A log message is shown when at least one + * checked category matches. Predefined keyword lists live in + * {@link LogOverlay} and stay in sync across game locales (FR + EN + * substrings are bundled together). + */ + public static class Categories { + @Option("halizeur.log_overlay.cat.gains") + public boolean gains = true; + + @Option("halizeur.log_overlay.cat.currencies") + public boolean currencies = true; + + @Option("halizeur.log_overlay.cat.resources") + public boolean resources = true; + + @Option("halizeur.log_overlay.cat.boosters") + public boolean boosters = true; + + @Option("halizeur.log_overlay.cat.errors") + public boolean errors = true; + + @Option("halizeur.log_overlay.cat.combat") + public boolean combat = false; + } } diff --git a/src/main/resources/dev/shared/lang/strings_en.properties b/src/main/resources/dev/shared/lang/strings_en.properties index 0b7a05cd..309980df 100644 --- a/src/main/resources/dev/shared/lang/strings_en.properties +++ b/src/main/resources/dev/shared/lang/strings_en.properties @@ -6,7 +6,14 @@ general.enabled=Enable # ----- Halizeur : Log Overlay ----- halizeur.log_overlay.config=Log Overlay halizeur.log_overlay.enabled=Enabled -halizeur.log_overlay.whitelist=gained,received,reward,earned,uridium,credit,honor,honour,experience,xp,prometium,endurium,terbium,prometid,duranium,promerium,seprom,xenomit,palladium,drop,booster,error,failed,refused,denied,unavailable,full,cannot,impossible +halizeur.log_overlay.categories=Categories to display +halizeur.log_overlay.categories.desc=Show only log messages whose content matches at least one checked category. +halizeur.log_overlay.cat.gains=Gains (loot, rewards) +halizeur.log_overlay.cat.currencies=Currencies (uridium, credits, honor, XP) +halizeur.log_overlay.cat.resources=Resources (prometium, endurium, …) +halizeur.log_overlay.cat.boosters=Boosters / drops +halizeur.log_overlay.cat.errors=Errors / refusals +halizeur.log_overlay.cat.combat=Combat / kills do_gamer.simple_healing.hp_repair=HP Repair (Solace, Orcus, Aegis, Hammerclaw) do_gamer.simple_healing.shield_repair=Shield Repair (Aegis, Hammerclaw) diff --git a/src/main/resources/dev/shared/lang/strings_fr.properties b/src/main/resources/dev/shared/lang/strings_fr.properties index aa14244c..78b78b03 100644 --- a/src/main/resources/dev/shared/lang/strings_fr.properties +++ b/src/main/resources/dev/shared/lang/strings_fr.properties @@ -6,7 +6,14 @@ general.enabled=Activer # ----- Halizeur : Log Overlay ----- halizeur.log_overlay.config=Log Overlay halizeur.log_overlay.enabled=Activé -halizeur.log_overlay.whitelist=gagn,obtenu,récup,recup,récompense,recompense,collect,ramass,uridium,credit,crédit,honor,honneur,experience,expérience,xp,prometium,endurium,terbium,prometid,duranium,promerium,seprom,xenomit,palladium,drop,booster,erreur,échec,echec,refusé,refuse,plein,indisponible,non disponible,interdit,impossible +halizeur.log_overlay.categories=Catégories à afficher +halizeur.log_overlay.categories.desc=N'affiche que les messages dont le contenu correspond à au moins une catégorie cochée. +halizeur.log_overlay.cat.gains=Gains (loot, récompenses) +halizeur.log_overlay.cat.currencies=Devises (uridium, crédits, honneur, XP) +halizeur.log_overlay.cat.resources=Ressources (prometium, endurium, …) +halizeur.log_overlay.cat.boosters=Boosters / drops +halizeur.log_overlay.cat.errors=Erreurs / refus +halizeur.log_overlay.cat.combat=Combat / kills do_gamer.simple_healing.hp_repair=Réparation des PV (Solace, Orcus, Aegis, Hammerclaw) do_gamer.simple_healing.shield_repair=Réparation du bouclier (Aegis, Hammerclaw) From e52f3e5a89d6913460d1218c72bd2962cd63c005 Mon Sep 17 00:00:00 2001 From: Halizeur Date: Sun, 10 May 2026 18:28:23 +0200 Subject: [PATCH 3/3] LogOverlay: use GameResourcesAPI translation matchers Per do-gamer review: drop the hardcoded FR + EN keyword list and match messages via GameResourcesAPI.getTranslationMatcher() on the official Bigpoint flashres keys instead. Categories Gains, Boosters, Errors and Combat are now driven by translation matchers; Currencies / Resources stay on substring matching since their names appear inside the %! placeholder of the gain templates (resource names are resolved via findTranslation("ore_*") so they still adapt to the active locale). --- .../halizeur/log_overlay/LogOverlay.java | 177 +++++++++++++----- 1 file changed, 129 insertions(+), 48 deletions(-) diff --git a/src/main/java/dev/shared/halizeur/log_overlay/LogOverlay.java b/src/main/java/dev/shared/halizeur/log_overlay/LogOverlay.java index 94c9a31a..9cc2fcf0 100644 --- a/src/main/java/dev/shared/halizeur/log_overlay/LogOverlay.java +++ b/src/main/java/dev/shared/halizeur/log_overlay/LogOverlay.java @@ -3,8 +3,10 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; +import java.util.EnumMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import eu.darkbot.api.PluginAPI; import eu.darkbot.api.config.ConfigSetting; @@ -18,6 +20,8 @@ import eu.darkbot.api.extensions.MapGraphics; import eu.darkbot.api.managers.EventBrokerAPI; import eu.darkbot.api.managers.GameLogAPI; +import eu.darkbot.api.managers.GameResourcesAPI; +import eu.darkbot.api.managers.GameResourcesAPI.TranslationMatcher; /** * Renders the latest in-game DarkOrbit log messages as an overlay on the @@ -27,10 +31,10 @@ * new system message. Each line disappears after DISPLAY_MS ms so the * canvas does not get cluttered. * - * Filter: only messages matching at least one keyword from the user's - * checked categories ({@link LogOverlayConfig.Categories}) are kept; - * keywords cover both FR and EN forms so the filter works regardless of - * the active game locale. + * Filter: per category, the overlay matches messages against translation + * patterns from {@link GameResourcesAPI} (the official Bigpoint flashres + * keys), so the filter works on every game locale without per-language + * keyword maintenance. */ @Feature(name = "Log Overlay", description = "Shows the latest in-game log messages as an overlay on the canvas", @@ -43,57 +47,117 @@ public class LogOverlay implements Behavior, Drawable, Listener, Configurable entries = new ArrayDeque<>(); + private final Map> matchers = new EnumMap<>(Cat.class); + private final List currencyNeedles = new ArrayList<>(); + private final List resourceNeedles = new ArrayList<>(); private LogOverlayConfig config; public LogOverlay(PluginAPI api) { api.requireAPI(EventBrokerAPI.class).registerListener(this); + + GameResourcesAPI res = api.requireAPI(GameResourcesAPI.class); + buildMatchers(res, Cat.GAINS, KEYS_GAINS); + buildMatchers(res, Cat.BOOSTERS, KEYS_BOOSTERS); + buildMatchers(res, Cat.ERRORS, KEYS_ERRORS); + buildMatchers(res, Cat.COMBAT, KEYS_COMBAT); + + for (String name : CURRENCY_NAMES) { + this.currencyNeedles.add(name); + } + for (String key : RESOURCE_KEYS) { + res.findTranslation(key).ifPresent(t -> { + String lower = t.toLowerCase(); + if (!this.resourceNeedles.contains(lower)) { + this.resourceNeedles.add(lower); + } + }); + } + } + + /** Pre-builds {@link TranslationMatcher} instances for every key in a + * category. Keys whose translation is missing in the active locale + * return {@link java.util.Optional#empty()} and are skipped silently. */ + private void buildMatchers(GameResourcesAPI res, Cat cat, String[] keys) { + List list = new ArrayList<>(); + for (String key : keys) { + res.getTranslationMatcher(key).ifPresent(list::add); + } + this.matchers.put(cat, list); } @Override @@ -125,23 +189,40 @@ public void onLogMessage(GameLogAPI.LogMessageEvent event) { } /** - * Whitelist filter: only display the message if at least one - * keyword from a checked category appears in it (case-insensitive). + * Filter: a message is kept when at least one checked category + * matches. Most categories use TranslationMatcher (built from the + * official Bigpoint translations); Currencies and Resources fall + * back to substring matching against localized names because the + * actual values appear inside the {@code %!} placeholder of the + * generic gain templates and are easier to detect this way. */ private boolean isAllowed(String msg) { - String lower = msg.toLowerCase(); LogOverlayConfig.Categories c = this.config.categories; if (c == null) return false; - if (c.gains && containsAny(lower, KW_GAINS)) return true; - if (c.currencies && containsAny(lower, KW_CURRENCIES)) return true; - if (c.resources && containsAny(lower, KW_RESOURCES)) return true; - if (c.boosters && containsAny(lower, KW_BOOSTERS)) return true; - if (c.errors && containsAny(lower, KW_ERRORS)) return true; - if (c.combat && containsAny(lower, KW_COMBAT)) return true; + + if (c.gains && anyMatcherFinds(Cat.GAINS, msg)) return true; + if (c.boosters && anyMatcherFinds(Cat.BOOSTERS, msg)) return true; + if (c.errors && anyMatcherFinds(Cat.ERRORS, msg)) return true; + if (c.combat && anyMatcherFinds(Cat.COMBAT, msg)) return true; + + if (c.currencies || c.resources) { + String lower = msg.toLowerCase(); + if (c.currencies && containsAny(lower, this.currencyNeedles)) return true; + if (c.resources && containsAny(lower, this.resourceNeedles)) return true; + } + return false; + } + + private boolean anyMatcherFinds(Cat cat, String msg) { + List list = this.matchers.get(cat); + if (list == null) return false; + for (TranslationMatcher m : list) { + if (m.find(msg)) return true; + } return false; } - private static boolean containsAny(String haystack, String[] needles) { + private static boolean containsAny(String haystack, List needles) { for (String n : needles) { if (haystack.contains(n)) return true; }