-
-
Notifications
You must be signed in to change notification settings - Fork 3
Fix viewer lookup, cache notice parts. #120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -19,7 +19,6 @@ | |||||
| import java.util.Collection; | ||||||
| import java.util.Collections; | ||||||
| import java.util.HashMap; | ||||||
| import java.util.HashSet; | ||||||
| import java.util.List; | ||||||
| import java.util.Locale; | ||||||
| import java.util.Map; | ||||||
|
|
@@ -78,13 +77,10 @@ public B player(UUID player) { | |||||
| @Override | ||||||
| @CheckReturnValue | ||||||
| public B players(Iterable<UUID> players) { | ||||||
| Set<VIEWER> viewers = new HashSet<>(); | ||||||
|
|
||||||
| for (UUID player : players) { | ||||||
| viewers.add(this.viewerProvider.player(player)); | ||||||
| this.viewers.add(this.viewerProvider.player(player)); | ||||||
| } | ||||||
|
|
||||||
| this.viewers.addAll(viewers); | ||||||
| return this.getThis(); | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -258,11 +254,10 @@ private void sendTranslatedMessages(LanguageViewersIndex<VIEWER> viewersIndex, T | |||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| Set<VIEWER> languageViewers = viewersIndex.getViewers(language); | ||||||
| TranslatedFormatter translatedFormatter = this.prepareFormatterForLanguage(language); | ||||||
|
|
||||||
| for (Notice notice : notificationsForLang) { | ||||||
| Set<VIEWER> languageViewers = viewersIndex.getViewers(language); | ||||||
|
|
||||||
| for (VIEWER viewer : languageViewers) { | ||||||
| Audience audience = audienceConverter.convert(viewer); | ||||||
|
|
||||||
|
|
@@ -298,23 +293,42 @@ private TranslatedNoticesIndex prepareTranslatedNotices(Set<Locale> languages) { | |||||
| }); | ||||||
| } | ||||||
|
|
||||||
| private final Map<Locale, TranslatedFormatter> formatterCache = new HashMap<>(); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The To ensure thread safety, you should use a thread-safe map implementation like
Suggested change
|
||||||
|
|
||||||
| protected TranslatedFormatter prepareFormatterForLanguage(Locale language) { | ||||||
| TranslatedFormatter cached = formatterCache.get(language); | ||||||
| if (cached != null && !cached.isDirty()) { | ||||||
| return cached; | ||||||
| } | ||||||
|
|
||||||
| TRANSLATION translation = this.translationProvider.provide(language); | ||||||
| Formatter translatedFormatter = new Formatter(); | ||||||
|
|
||||||
| for (Map.Entry<String, TextMessageProvider<TRANSLATION>> entry : this.placeholders.entrySet()) { | ||||||
| translatedFormatter.register(entry.getKey(), () -> entry.getValue().extract(translation)); | ||||||
| String value = entry.getValue().extract(translation); | ||||||
| translatedFormatter.register(entry.getKey(), value); | ||||||
| } | ||||||
|
|
||||||
| return new TranslatedFormatter(translatedFormatter); | ||||||
| TranslatedFormatter formatter = new TranslatedFormatter(translatedFormatter); | ||||||
| formatterCache.put(language, formatter); | ||||||
| return formatter; | ||||||
| } | ||||||
|
|
||||||
| protected class TranslatedFormatter { | ||||||
|
|
||||||
| protected final Formatter translatedPlaceholders; | ||||||
| private final int expectedFormatterCount; | ||||||
| private final int expectedPlaceholderCount; | ||||||
|
|
||||||
| protected TranslatedFormatter(Formatter translatedPlaceholders) { | ||||||
| this.translatedPlaceholders = translatedPlaceholders; | ||||||
| this.expectedFormatterCount = NoticeBroadcastImpl.this.formatters.size(); | ||||||
| this.expectedPlaceholderCount = NoticeBroadcastImpl.this.placeholders.size(); | ||||||
| } | ||||||
|
|
||||||
| protected boolean isDirty() { | ||||||
| return NoticeBroadcastImpl.this.formatters.size() != expectedFormatterCount | ||||||
| || NoticeBroadcastImpl.this.placeholders.size() != expectedPlaceholderCount; | ||||||
| } | ||||||
|
Comment on lines
+329
to
332
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The A more robust approach would be to use a modification counter (e.g., an |
||||||
|
|
||||||
| public String format(String text, VIEWER viewer) { | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lazy initialization of
cachedPartsis not thread-safe. If multiple threads callparts()concurrently whencachedPartsisnull, they might all enter thisifblock, leading to redundant computation. While this is a benign race condition in this specific case, it's better to use a standard thread-safe lazy initialization pattern.One option is to use double-checked locking, which would require making
cachedPartsvolatile.Alternatively, a simpler and safer approach would be to initialize
cachedPartsin the constructor and make itfinal, since thepartsmap is effectively immutable after construction.