LoChat/TODO.md
loki5512344 a822c91637
feat: chat channel system (/join /leave /channel)
- 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
2026-08-09 21:31:11 +02:00

8 KiB
Raw Permalink Blame History

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 blocking applyTranslation() from render() (zero network I/O in render path). Translation is now delivered via async action bar (sendTranslationAsync → translateAsync().thenAccept).
  • translate/TranslationService.java — no more String.intern(); bounded LRU cache (2000 entries, synchronized LinkedHashMap); translateAsync runs 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/saveJson now write <name>.tmp + Files.move(ATOMIC_MOVE, REPLACE_EXISTING) under a per-file ReentrantLock.
  • core/service/PlayerServiceImpl.java — statsLock serializes async persistPlayerMessages and main-thread saveAll().
  • core/service/PunishmentServiceImpl.java — removed saveAsync() from isBanned() read path.
  • core/service/mute/MuteDataStorage.java — uses FilePersistence.saveJson (atomic), dropped raw FileWriter.
  • core/service/mute/MuteHistoryManager.java — atomic temp+rename write under per-file lock, no e.printStackTrace().

3. Route /g and /l through the same filter pipeline + escape MiniMessage ✅

  • core/service/ChatServiceImpl.java — String messages now run length check → MessageService.processMessage (mute/cooldown) → AdvancedMessageFilter → MiniMessage.escapeTags() → component (colors only with lochat.chat.colors). Component messages bypass (trusted internal). Local radius from AppearanceConfig.
  • commands/chat/GlobalChatCommand.java, LocalChatCommand.java — pass plain String, no more MM.deserialize(raw input).
  • LoChat.java/PluginInitializer.java — filter instance stored on plugin; ServiceRegistry builds MessageService before ChatService.

4. Remove duplicate Discord send; single global/local detection ✅

  • listener/DiscordEventListener.java — deleted the onChat(AsyncChatEvent) handler; ChatEventListener is now the single send path with correct isGlobal.

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.yml is 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 keys gradient.limits.*, gradient.pricing.*, gradient.cooldowns.*, gradient.formatting.*, gradient.storage.type → SQLite now detected.
  • managers/AutoMessageManager.java + config.yml — reads automessages.messages (new automessages: section added to config.yml), honors automessages.random.

✅ COMPLETED THIS SESSION

Architecture (SOLID/KISS/DRY)

  • AppearanceConfig — fixed double-call bug
  • config/ — 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 → FilterPipeline with dynamic registration
  • 25 commands → BaseCommand/AdminCommand/PlayerCommand hierarchy
  • MuteCommand — decomposed into helper methods (KISS fix)

Encapsulation

  • ChatEventListener — removed instanceof cast to PlayerServiceImpl
  • PunishmentSnapshot — mutable fields → private + getters
  • BaseConfig — 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: ChatEventListener sends to the joined channel; local radius filtering; per-channel cooldown; channel-aware EnhancedChatRenderer.
  • 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-version hint / provides in plugin.yml for dependency resolution.
  • Dedicated API events: LoChatMessageEvent, LoChatFilterEvent, LoChatPunishEvent etc.

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)