- PlayerServiceImpl: separate cooldown map per chat type (fixes cross-blocking between local chat and RP commands) - PrivateMessageServiceImpl: enforce chat.pm.cooldown (honors chat.bypass.cooldown) - NickServiceImpl: persist nicknames async on every change/reset - FloodFilter/SpamFilter: ConcurrentLinkedDeque instead of ArrayDeque - tests: PlayerServiceCooldownTest
7.6 KiB
LoChat Development TODO
Last updated: 2026-08-04
✅ CRITICAL FIXES (blocking release — from code review 2026-08-04, all DONE)
1. Translation: async + bounded cache, no String.intern(), out of render() ✅
renderer/EnhancedChatRenderer.java— removed blockingapplyTranslation()fromrender()(zero network I/O in render path). Translation is now delivered via async action bar (sendTranslationAsync→translateAsync().thenAccept).translate/TranslationService.java— no moreString.intern(); bounded LRU cache (2000 entries, synchronized LinkedHashMap);translateAsyncruns on a dedicated 2-thread daemon executor; failures return original text, never throw.
2. Atomic file writes (temp + rename, per-file lock) for all persistence ✅
utils/persistence/FilePersistence.java—saveYaml/saveJsonnow write<name>.tmp+Files.move(ATOMIC_MOVE, REPLACE_EXISTING)under a per-fileReentrantLock.core/service/PlayerServiceImpl.java—statsLockserializes asyncpersistPlayerMessagesand main-threadsaveAll().core/service/PunishmentServiceImpl.java— removedsaveAsync()fromisBanned()read path.core/service/mute/MuteDataStorage.java— usesFilePersistence.saveJson(atomic), dropped raw FileWriter.core/service/mute/MuteHistoryManager.java— atomic temp+rename write under per-file lock, noe.printStackTrace().
3. Route /g and /l through the same filter pipeline + escape MiniMessage ✅
core/service/ChatServiceImpl.java—Stringmessages now run length check →MessageService.processMessage(mute/cooldown) →AdvancedMessageFilter→MiniMessage.escapeTags()→ component (colors only withlochat.chat.colors).Componentmessages bypass (trusted internal). Local radius fromAppearanceConfig.commands/chat/GlobalChatCommand.java,LocalChatCommand.java— pass plainString, no moreMM.deserialize(raw input).LoChat.java/PluginInitializer.java— filter instance stored on plugin;ServiceRegistrybuildsMessageServicebeforeChatService.
4. Remove duplicate Discord send; single global/local detection ✅
listener/DiscordEventListener.java— deleted theonChat(AsyncChatEvent)handler;ChatEventListeneris now the single send path with correctisGlobal.
5. Fix config key mismatches (underscore vs dash / nesting) ✅
config/AppearanceConfig.java—chat.local-radius,chat.clear-lines,chat.min-message-length,chat.max-message-length,chat.colors.*(dash),prefixes.*.separator(string),discord.avatar-urls.*,discord.event-titles.*(+_→-normalization).integrations/discord/DiscordConfig.java—webhook.avatar-url,chat.global-only,chat.use-embed,chat.embed-color,filter.*(dash),performance.retry-*.config/MuteConfig.java—settings.*,notifications.*.config/SoundsConfig.java—messages.mention.*,messages.private-message.*,commands.success.*.config/FiltersConfig.java+ filters —config/filters.ymlis now the single source of truth (caps.max-caps-percent,swear.replace,hidden-urls.*,spam.max-repeating-chars,flood.max-same-messages).gradient/config/GradientConfig.java— nested keysgradient.limits.*,gradient.pricing.*,gradient.cooldowns.*,gradient.formatting.*,gradient.storage.type→ SQLite now detected.managers/AutoMessageManager.java+config.yml— readsautomessages.messages(newautomessages:section added to config.yml), honorsautomessages.random.
✅ COMPLETED THIS SESSION
Architecture (SOLID/KISS/DRY)
AppearanceConfig— fixed double-call bugconfig/— 9→3 files per folder (subfolders: chat/, filter/, mute/, manager/)api/service/+core/service/— 8→3 files per folder (subfolders: chat/, player/, moderation/, pm/, spy/, ignore/)ConfigManager— god class → facade + 6 managers (Chat/Pm/Mention/ClearChat/CustomMessages/Filters)MessagingService— fat interface → 3 services (PrivateMessageService/SpyService/IgnoreService)AdvancedMessageFilter— hardcoded pipeline →FilterPipelinewith dynamic registration- 25 commands →
BaseCommand/AdminCommand/PlayerCommandhierarchy MuteCommand— decomposed into helper methods (KISS fix)
Encapsulation
ChatEventListener— removed instanceof cast toPlayerServiceImplPunishmentSnapshot— mutable fields → private + gettersBaseConfig— protected fields → private + protected getters- 29 files with
import.*→ explicit imports
Checkstyle
- Config: 4-space indent, 140-char lines, Google-style naming/quality
- Warnings: 520 → 0 (both main and test)
- Wired into
build.gradle.kts
Configs
- All rewritten: clean, English comments, no Spacelegacy palette
🟡 NEXT: NEW FEATURES
1. Channels (like VentureChat / Carbon)
/join,/leave,/channel— full channel system: global, local, staff, trade, party.- Per-channel colors, aliases, permissions, cooldowns, spy mode.
- Party chat: private groups
/party. - Network channels (Bungee/Velocity) sync.
2. Anti-swear hardening + filter statistics
- Bypass obfuscation:
q→рleet/Unicode substitution, repeated chars between letters, spaces/dots inside words. - Track filter stats (how many messages blocked per filter) → expose via command/placeholder.
3. Stable plugin API + API events
- Publish
api/modules to a repo so third-party plugins can depend on it. - Add
api-versionhint /providesinplugin.ymlfor dependency resolution. - Dedicated API events:
LoChatMessageEvent,LoChatFilterEvent,LoChatPunishEventetc.
4. DiscordSRV-style 2-way bridge (Discord → chat)
- Read messages from a Discord channel (bot or webhook listener) and relay into Minecraft chat.
- Channel ↔ Discord channel mapping.
5. Full spy + auto-mute for spam
- PM spy + command spy with custom formats (like VentureChat).
- Auto-mute repeat offenders (spam/flood) with configurable threshold and duration.
🔵 LOWER PRIORITY (carry-over)
1. Shared Persistence Utility
5 services repeat load/save/ensureDir pattern:
NickServiceImpl,PlayerServiceImpl,ChatServiceImpl,PunishmentServiceImpl,IgnoreServiceImpl
Fix: Extract FilePersistenceUtil or base class
2. Persist nicknames on change ✅
NickServiceImpl now saves asynchronously (FoliaUtil.runAsync → save()) on every setNickname/resetNickname — nicknames survive crashes.
3. DIP: Service Locator Anti-Pattern
ServiceRegistry.get(Xxx.class) used everywhere instead of constructor DI. Big refactor.
4. DRY: PM Send Logic ✅
PM cooldown + actual send centralized in PrivateMessageServiceImpl.sendPrivateMessage; both /msg and /reply delegate to it.
5. Fix cooldown map collision ✅
PlayerServiceImpl now keeps a separate ConcurrentHashMap per chat type (global, local, rp_me, rp_do, rp_try, pm) — no more cross-blocking. Covered by PlayerServiceCooldownTest.
6. Thread-safe flood/spam deques ✅
FloodFilter/SpamFilter now use ConcurrentLinkedDeque instead of plain ArrayDeque.
7. Tests
Only 11 test files for 155 main files. Very low coverage (JaCoCo wired, CI uploads report).
8. No PM cooldown ✅
chat.pm.cooldown (default 2s) is now enforced in PrivateMessageServiceImpl.sendPrivateMessage, honoring chat.bypass.cooldown.
📋 KNOWN ISSUES
- Gradients may not display correctly on clients < 1.16
- Some filters may block legitimate messages (configurable in
config/filters.yml)