- config-driven channels in config/channels.yml (global, local, staff, trade) with emoji, colors, prefix, radius, cooldowns, permissions and aliases - Channel record + ChannelConfig loader wired into ConfigManager (reload supported) - ChannelService (api) + ConcurrentHashMap-backed ChannelServiceImpl, joined in ServiceRegistry; default channel join treated as 'not in channel' - JoinChannelCommand / LeaveChannelCommand / ChannelCommand (+ tab completion), registered in CommandManager; plugin.yml commands + permissions - ChatEventListener routes AsyncChatEvent to the active channel: per-channel cooldown, local-radius viewer filtering, channel-aware renderer - EnhancedChatRenderer supports Channel overload (emojiFor/prefixText/channelColor) - ChannelServiceImplTest covering join/leave/permissions/aliases
8 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.- Config-driven channels via
config/channels.yml(emoji, colors, prefix, radius, cooldowns, permissions, aliases). - Chat routing:
ChatEventListenersends to the joined channel; local radius filtering; per-channel cooldown; channel-awareEnhancedChatRenderer. - Party chat: private groups
/party(future). - Network channels (Bungee/Velocity) sync (future).
2. Anti-swear hardening + filter statistics ✅
- Obfuscation bypass:
SwearNormalizer(q→р / leet-подстановки, повторы букв «сууука», разделители внутри слова «с.у.к.а» / «с у к а») + tolerant-маскирование в replace-режиме. Single source of truth для Aho-Corasick. - Filter stats:
FilterStatisticsсчитает блокировки по каждому фильтру →/lochat filterstats.
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)