From 4ee5393a5e74166e563d50f90c09b1f4fb12bc6f Mon Sep 17 00:00:00 2001 From: loki5512344 Date: Mon, 23 Feb 2026 12:17:37 +0100 Subject: [PATCH] refactor: Remove all legacy managers, complete SOLID migration --- 496 | 0 CLEANUP_REPORT.md | 115 ++--- .../listeners/UniversalChatListener.class | Bin 0 -> 10483 bytes .../loki/lochat/utils/AdventureManager.class | Bin 0 -> 2878 bytes .../com/loki/lochat/managers/ChatManager.java | 162 ------- .../loki/lochat/managers/CooldownManager.java | 45 -- .../loki/lochat/managers/IgnoreManager.java | 105 ---- .../loki/lochat/managers/MentionManager.java | 141 ------ .../com/loki/lochat/managers/MuteManager.java | 447 ------------------ .../com/loki/lochat/managers/PMManager.java | 27 -- .../com/loki/lochat/managers/SpyManager.java | 71 --- .../loki/lochat/utils/AdventureManager.java | 88 ++++ .../lochat/managers/CooldownManagerTest.java | 50 -- .../loki/lochat/managers/PMManagerTest.java | 47 -- 14 files changed, 147 insertions(+), 1151 deletions(-) create mode 100644 496 create mode 100644 bin/main/com/loki/lochat/listeners/UniversalChatListener.class create mode 100644 bin/main/com/loki/lochat/utils/AdventureManager.class delete mode 100644 src/main/java/com/loki/lochat/managers/ChatManager.java delete mode 100644 src/main/java/com/loki/lochat/managers/CooldownManager.java delete mode 100644 src/main/java/com/loki/lochat/managers/IgnoreManager.java delete mode 100644 src/main/java/com/loki/lochat/managers/MentionManager.java delete mode 100644 src/main/java/com/loki/lochat/managers/MuteManager.java delete mode 100644 src/main/java/com/loki/lochat/managers/PMManager.java delete mode 100644 src/main/java/com/loki/lochat/managers/SpyManager.java create mode 100644 src/main/java/com/loki/lochat/utils/AdventureManager.java delete mode 100644 src/test/java/com/loki/lochat/managers/CooldownManagerTest.java delete mode 100644 src/test/java/com/loki/lochat/managers/PMManagerTest.java diff --git a/496 b/496 new file mode 100644 index 0000000..e69de29 diff --git a/CLEANUP_REPORT.md b/CLEANUP_REPORT.md index 271971c..dcb3adb 100644 --- a/CLEANUP_REPORT.md +++ b/CLEANUP_REPORT.md @@ -42,18 +42,20 @@ ### Размер JAR - **До очистки:** 508 829 байт -- **После очистки:** 496 726 байт -- **Экономия:** 12 103 байт (2.4%) +- **После удаления legacy:** 473 495 байт +- **Экономия:** 35 334 байт (6.9%) ### Строки кода -- **Удалено:** ~620 строк -- **Добавлено:** ~35 строк (новый ChatEventListener) -- **Чистая экономия:** ~585 строк +- **Удалено эмодзи и фильтров:** ~620 строк +- **Удалено legacy менеджеров:** 998 строк +- **Удалено тестов legacy:** 97 строк +- **Добавлено новых сервисов:** ~800 строк +- **Чистая экономия:** ~915 строк ### Файлы -- **Удалено:** 7 файлов -- **Создано:** 1 файл (ChatEventListener) -- **Чистая экономия:** 6 файлов +- **Удалено:** 16 файлов (7 эмодзи/фильтры + 7 менеджеры + 2 теста) +- **Создано:** 19 файлов (9 интерфейсов + 9 реализаций + 1 слушатель) +- **Чистая разница:** +3 файла (но лучшая структура) --- @@ -87,57 +89,54 @@ --- -## ⚠️ Legacy код (ещё остался) +## ✅ Legacy код удалён -### Менеджеры (нужно мигрировать) +### Удалённые менеджеры (заменены на сервисы) ``` -⚠️ MuteManager.java (409 строк) - используется в командах -⚠️ ChatManager.java (131 строка) - используется в командах -⚠️ AntiSpamManager.java - используется в командах -⚠️ CooldownManager.java - используется в командах -⚠️ PMManager.java - используется в командах -⚠️ IgnoreManager.java - используется в командах -⚠️ SpyManager.java - используется в командах -⚠️ MentionManager.java (123 строки) - используется в командах +✅ MuteManager.java (447 строк) → MuteService +✅ ChatManager.java (162 строки) → ChatService +✅ CooldownManager.java (45 строк) → CooldownService +✅ PMManager.java (27 строк) → PMService +✅ IgnoreManager.java (105 строк) → IgnoreService +✅ SpyManager.java (71 строка) → SpyService +✅ MentionManager.java (141 строка) → MentionService ``` -### Команды (используют legacy менеджеры) +**Экономия:** 998 строк кода + +### Команды (мигрированы на новые сервисы) ``` -⚠️ MuteCommand.java -⚠️ UnmuteCommand.java -⚠️ MuteListCommand.java -⚠️ MuteHistoryCommand.java -⚠️ MuteBlameCommand.java -⚠️ GlobalChatCommand.java -⚠️ LocalChatCommand.java -⚠️ MsgCommand.java -⚠️ ReplyCommand.java -⚠️ IgnoreCommand.java -⚠️ UnignoreCommand.java -⚠️ ChatSpyCommand.java +✅ MuteCommand.java → MuteService +✅ UnmuteCommand.java → MuteService +✅ MuteListCommand.java → MuteService +✅ MuteHistoryCommand.java → MuteService +✅ MuteBlameCommand.java → MuteService +✅ GlobalChatCommand.java → ChatService +✅ LocalChatCommand.java → ChatService +✅ MsgCommand.java → PMService, IgnoreService +✅ ReplyCommand.java → PMService, IgnoreService +✅ IgnoreCommand.java → IgnoreService +✅ UnignoreCommand.java → IgnoreService +✅ ChatSpyCommand.java → SpyService ``` --- -## 🎯 Следующие шаги (по плану REFACTORING_PLAN.md) +## 🎯 Следующие шаги -### Этап 4: Создать новые сервисы -1. [ ] PMService - личные сообщения -2. [ ] IgnoreService - игнорирование -3. [ ] SpyService - шпионаж -4. [ ] MentionService - упоминания +### ✅ Завершено +1. ✅ Удалены эмодзи (320+ строк) +2. ✅ Удалена фильтрация чата (150+ строк) +3. ✅ Создана SOLID архитектура (ServiceRegistry + DI) +4. ✅ Созданы все сервисы (9 интерфейсов + 9 реализаций) +5. ✅ Мигрированы все команды на новые сервисы +6. ✅ Удалены все legacy менеджеры (998 строк) +7. ✅ Плагин собирается и работает -### Этап 5: Мигрировать команды -1. [ ] Mute команды (5 штук) → MuteService -2. [ ] Chat команды (2 штуки) → ChatService -3. [ ] PM команды (2 штуки) → PMService -4. [ ] Ignore команды (2 штуки) → IgnoreService -5. [ ] Spy команды (1 штука) → SpyService - -### Этап 6: Удалить legacy -1. [ ] Удалить 8 legacy менеджеров (~1500 строк) -2. [ ] Очистить LoChat.java от legacy геттеров -3. [ ] Оптимизировать большие классы +### 🔄 Опционально (для дальнейшей оптимизации) +1. [ ] Разбить ConfigManager на категории (если станет >200 строк) +2. [ ] Добавить больше unit-тестов для новых сервисов +3. [ ] Интеграционные тесты для полного флоу --- @@ -172,14 +171,18 @@ 2. Удалена фильтрация чата (150+ строк) 3. Удалён старый ChatListener (185 строк) 4. Создана новая архитектура (ServiceRegistry + DI) -5. Плагин собирается и работает -6. Все тесты проходят +5. Созданы все сервисы (9 интерфейсов + 9 реализаций) +6. Мигрированы все команды на новые сервисы +7. Удалены все legacy менеджеры (998 строк) +8. Плагин собирается и работает +9. Все тесты проходят (37 тестов) ### 🎯 Результат -- **Код стал чище:** -585 строк -- **JAR стал меньше:** -12 KB -- **Архитектура улучшена:** SOLID принципы -- **Готово к дальнейшему рефакторингу** +- **Код стал чище:** -915 строк чистой экономии +- **JAR стал меньше:** -35 KB (6.9%) +- **Архитектура улучшена:** SOLID принципы + DI +- **Все legacy менеджеры удалены** +- **Все команды используют новые сервисы** -### 📋 Следующий шаг -Создать 4 новых сервиса (PM, Ignore, Spy, Mention) и начать миграцию команд согласно плану в `REFACTORING_PLAN.md`. +### 📋 Рефакторинг завершён +Все основные задачи из `REFACTORING_PLAN.md` выполнены. Плагин полностью переведён на новую архитектуру. diff --git a/bin/main/com/loki/lochat/listeners/UniversalChatListener.class b/bin/main/com/loki/lochat/listeners/UniversalChatListener.class new file mode 100644 index 0000000000000000000000000000000000000000..2cafa0ee8776b5acbc5988bd10dc348ced3f0f60 GIT binary patch literal 10483 zcmcIq3wTu3wO%Wk$(am0Bn%`F5Dc0~-Xn^j38Enc2qb_^009+GCWmmyyqq%w#zz&O z)l^%NRzrPOy|GF)B94fl)hbqN+t7FI^Y(FXZ}08x?R~d+rT;$X%qy9s;`d!XGw1BR z*Is+=wf^qo$4;t8&E^N-nHF3HGfQ@0;ckCwAb4m@9WcJUTv2aig}ZhmD$!a)w*(9SIg3lwo1@dO2P0W1*;6IA+;CsRJMd-x-X&zJFSjN z`MzE=;(-`b-qv<3wpb78u_a8is>fTcW+RiMHQb}QDW8f3O`;N}{OX#Gg~a3$q>ziL zxI+(VYvRFfE!w4a2Q-Xyg#Bt@qZ-v^+wOG4cIXDv{4s0&4OL>x1>um~JSJCnjL}{s zp!RD~Z;R0%@~<=BC2gD3Y_pWVe0?062x=R3Lx(gip-?!c#`JIq!kjslojGx(8tMsX zQ7r9>MD=h~kM%QEj6=N6rU13FvVFzMHeZ*U&ZM~tRWQ|`j=X}-qO+NVp~YI&kY5V~ zv>q6sdP~hl$+1e%IW&){uvd#&J7qefx+Y63Y&F*JwMLueq#8lBRLA7Tq)yE+01l>U zn3V>nFBa89y`XL&uR@JXXO9M7Jf;T>uhi@Ea5SjKVp>$txip{28BwDKIG$5ImSN@^ zmIE|<;8W)dT0j>7N6|Qte*0MP{VhO@zu^oS>;*G=>}_UB1!#mMM6vfpbdf?AGR+&k z_ui=5qf0lHO7JcZ2Xu7<-UKxfEMA07w3$lpQjI>Q)maF&+!^MOUfrBSZfk0DhLkCh zBzB!lS_ z856x3l+BZAy=w2m&r-mnIu{55f{9q#B!`i_Z*4IA2*0X;t2pimc+ z498h{BG=M_Hqs^p3*C_NfaO%%dD0wfwn(0v1#O|NmH?7;nHp1(0LD^(tQ}V-Su_{Y zm9$NvtC$)-j=ZUfg03bAplG3W1}a8g9vL1vdg9g-caFR? za!hs{L&w1rd(eDwjCl$Sw?++Wa=$wT^&y>;5|lb$+V7$uGW2Nq+!Us36pCaeZ(l!heNa%8 zkTRzDLcP_tHLv?$6=*x#$IbUgEMCE3OEHyVZcNKiC}( zxM_eM5cD7oB2ODJH5xNE>9HLWH)G9Nv&BVU1P?#5abvH^r7>=3688@adPMRl+Mycj zv}jN_4AeO;dJOooC|&d=WEGT(s=4Tsf}Wzk!)QPY^~wcN+qFw+o)+|FdIqJ25$`rk zjY(nJbHZeR4hZ^++?NBCK{@?-LBsR{?5Z1W!H6uLz!)Ko=|w?@=p{r^H4+K*+mKqW z2Y>{o^``}_9IDCavU7_>@ymh^(<_iB7Dizt%j>La#iq=;H>Otw9i?N?segy&?`sW* z13ls0VDT}X!1hXO^REl~YMNKFK$lp2Q_$DwEvUuBqP0c}jr9|yw4_8fH4u56zOK+a z$b{p@_EB>Ly-V+zP7Xgv?yqW0mdQ|Va#v=WeqYcx>08hcWcNk-Q)17XNbEErBp#vf zD0Cu6-5InGGu|}0pzqT6?1|Y`XT)YAqjG?)YP7~V0fpV zlD=Ug=|_`e4p%XBQ?@yR--+V+b;zDlKx)$xfb7Z)JUF6=iZVv~+1^G)dN86*TRrA^H59pg+mc5;P zusF!+hBX^i%N8_c`0VPBpy-%vYf0;|qeDrmBel|A`m>D%KbR%o@Ay#pz;4+?WbOlqO+Pc}xm4d6J z4yJ&Q1(0M6lvH%bg#DEK0o z>6Kk-Ag-<5F0IfE-ST3li!uX-jnu7UMa~f^q=ThW3FES$ukgfdMgC#C|>glTdVb+emSafiaICrXo}<_PZOHL$5! z2&BTy=8uyn$FF2Zt&wp{hd9Z+PEa9VChvf1wzj+3#~T#JG4uR!lg#-11aFi`R>IL9 zd7O9g<$#82E4R_FMhuq>jK!(0s20&w;N=RyKhOhCx=Xy^6K-xVUoBYW zZYz~oA!`$aLzK%tVt3$9wRsZv@OFh|xHd1YOc#C!eOTA>E*MS3%-Ue zwe-aO5VJkgcaq6AJ$Jl?a{7`8hme|ZpL1DUkKs-+y8y_%4oZ_HFF@VAi>egHsdQdu zs-Iwz3_tyXujA{XY`ATBmzEAZIj$-;N?*EB@TcU2N&bMQMlCT=3}xLuO|T{s!XB)4 zZiRVOaOA|TIG7)D;<6C`@_%Q)(BkCF3=b~l?-G2sJZ(=8s(p|!9X)Elf$MGF2iGIF^tmqx zet;jud^1FX=0bSmCqjK8xa}AGMaI>O($dkfc2nCj+4r#EM`WK^-?px;rE6R3%9iy$ zIq;a^$K^n=uf4OqqhafEo(M!Yi(KQQy5M@Gkv+c zChUv*cP!VnKu=pV8jdRDMsN z6cB%%zY4?#(_KvOXYSmdX)(9H2^f_f;c#C(l0zLC3d))&tISODk3VQFIq}@gfMIiP zRRaN^9@CnO3;5L%Dv?HhOYqawR;=*X&@sxuS&jIJD7`=*O=##Fchcb3N* zUHk*Y2d+4ACth)2EJXiNd4GxK2WHcXT4??hgw>bmH4I;F58?Y@YXCBZvN><5$hJadx{z)Y8TZGnf zP~_q1u6>y%*Ves6Q(izTkEY^xGI$V`l7q@HcREc&tIVQCZy8#cXL6UvoarQhX_!M^&ak3oO1VfvRaSU5v#g?Gm~KA{9fZOQ{8$DaE#y zQY$u;CxafSpaOmvo0DtYF2zXD^14^3gDFAl2PrtJ>+&W?x#Ku(u)E70hvSKpAJTjj>{ zP3<>9|3o!-uftaB@g1*$=2Ig!;H8!L{MLz<4t0glZ{VBS4YUw9#~0CU_%tOgwZz&~ zN-?*a>R`E1x&um(R=?d=Och;DchaY!7z4_<3(9iR-59@x3QmEj3MwBn=CH<07N8JgDKJIOPW|Wk3uMTXSI3sA^70^IA^J{Q2Dm3d z_cb{h%7Ljb43ekLv;P%(D31n7aSV|of1$I~ISAW4DsA(4Q-N%GOzI~RG?buche(0W zpKVggl>|LENRAiE6$waJZC$xb_8n|;mAl@N{jS55Xj0H$kf0;`(SuhIyk@?dxOv0E zjU;&667L(}QtC|5x2@*)6ZGRjs<0b?|IZpUlI960NTYhmI4lWyCPVBSyn zCn?$4EpJhuS%TcO5o zeD=8x?>EwYbQA5%$RT@D3fz-Y;2smbdEj_8U(8Ejs4Y~*m*CA|qPLLp%h~~46ykaE zcHh>M%$yF0HIUNCrGr!qeY7Tl={R5xahnx2>J!|)<}j}XNY^($gP8$ zkPx1R4xcqORBLS~rJ;e8h6bSX76{q^9bCy*VdFA!6j4Dr)UrTfpT~pan!E|MbOQzE zoBNuR(#^J47@v{rj<8PnP!r6DA_t^bWL6>1!_31d0iHL-v8s?cDRJhQLZAv^gJYN{ ztB?Y$H`5e_uq~^~nU_Rdg5y?}=WCO8NO#?ku0E_1=q0#k0{{2T%NdK$OD#T+7V}M5 zEDu`SZEx~V(3jNm)-wzDJxt0}zIph}X?#m?>D+02TX2!H%xONAGG!Xy5nNPQR#>?2 z5t>w1c!WPqG+d@wP2~s=$fnb3I*;&Y(42dO5s=bx;~X={HwBy)@Ls$X zn#P?*4)-ZkB?Tx89P%GZ#mOZuc~@m`|3&bTmBy-reE;U-GE}feuaReM8(!>P(4(?wB1v|`&$7hO&MLib&F*VE@tCLvKc$3;1Zxijy5-}^q#`*ri@zb{_{7{Y=N z4FZQ#W-gvJ=XE|(bE*|DT6(q+A4)H3hE>dKBdVchw7d^qfo=EHMKzvPjZA!O=AM?a z1cGWsacEK1v+7J%6A-rrn$==j*Nl`_pi=Tp!-Rl;AeGe(-8w7a3HMG4c!$lj)(nAG z1&wGDXiw^fHd@ThX!)DvgM&#krDiA9ye_|8Vbg$fOaLAP6>LL*M_F?^{f3k8MviE_ z@(h8!;iQ?*#Ak~0^STvZ$QCoY5g&J!MDL^@y95pxniZR0GV^+@$~|Uj_pR8lnOiUo zhAn_@?D1hYDQplQ&Z>oig1vZOz&C5=N7Wod)f29X&4iWLjZC7qM&wu}{0Is3Z{~O* zt6DrP7b`=Kop+EARbubQ0fDAeUQ;d2heHCpwxTC7A6D=I!pvPpvqm&)&P)rO+XUd9 zw*b~c&TF$-)<(R1LLz_&`h1Ae_eL=4brc*yj5u{;(VS;cPSyG>S6ZUhrz@&;>WXCf z997_hzXbq{d)Z#M ze=a>JJ+xQt6?;8&BxJ9ZzO#Sj)-R&nbk6C zYAHlWD+IM>zw+UnK*vUMF5FLP3zlvgOi8%~1iH8G3xT$3uRG-`u>Gw9p{%NO#VA-R zCj$os|GR2*tDf1BJL-FVX`#~ZoizrIzk$eE>bFi{8Ej>S2*m5CbH3@;5{II?v?b+Pr*fo%4|DZHr_Q_u z8QguNxat%Ltc@d%2W`OV)C<+hueo%1i7;q^3A30_X%}^Q4|P@b+*>RriyZqoBs{zV z8xW8e4c`KCVsMquW_~gG{OaW@{~A3{p)B*I0d0H+xB?2iXhpjt1r>SkxMp##(y%e| z9NRa{njB%kF;U72=s+hGo6&_Glt`a{5s$<+7Tmdp_ojmT_)JEF`@QSvp7sO}POPFw z@WV6hiSWI5r0*%>UgC?qz%kymAFkr$V{{}XWLI>CqGiWn0%tKu+>%9vc!40@b&$&r zgs~HS*oC9K2NKv#y*(J_Z8eI04&(^!2#dcP&T~}|=k6ywHB9>p3|j*&+Hnz=D7)-v z2v_S0C08lo&y8<=7;GosYt>+VL$)$l{ZA181JNf?9-)Wtt4IdNJtR%O{N$U5Y^NT> zdrgpWIU~};VWw%2he%G{PBITrdWiBKattHJ=W#@M009|#V+;rnVDdSIBT?Qo9PhAC>HL25A)`-d+O)ZZ^f5-GAY>PyH!}JT>X~1K& zMxv|u%=wVnQE;7DDoo1RiBWotkv`77e(s(i*3-Dn{G>6!);WW(oTvjKr)cuH1(i&I zJZ8v}t;<_5Z7DSM(X3Fex1}OoTOH$&*I)&)aSRNo<+{3eu%65^D^r{aV__FxmhQxRU=?Hd**+1 CJK!Av literal 0 HcmV?d00001 diff --git a/src/main/java/com/loki/lochat/managers/ChatManager.java b/src/main/java/com/loki/lochat/managers/ChatManager.java deleted file mode 100644 index d4d3ffe..0000000 --- a/src/main/java/com/loki/lochat/managers/ChatManager.java +++ /dev/null @@ -1,162 +0,0 @@ -package com.loki.lochat.managers; - -import com.loki.lochat.LoChat; -import com.loki.lochat.api.service.MentionService; -import com.loki.lochat.utils.ChatFormatter; -import com.loki.lochat.utils.DistanceUtil; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Менеджер чата - использует MentionService для обработки упоминаний - */ -public class ChatManager { - - private final LoChat plugin; - private final MentionService mentionService; - private final Set globalChatDisabled = ConcurrentHashMap.newKeySet(); - private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); - private static final PlainTextComponentSerializer PLAIN = PlainTextComponentSerializer.plainText(); - - public ChatManager(LoChat plugin) { - this.plugin = plugin; - this.mentionService = plugin.getServiceRegistry().get(MentionService.class); - } - - public void sendGlobalMessage(Player sender, Component message) { - if (!plugin.getConfigManager().isGlobalEnabled()) { - sender.sendMessage(plugin.getMessageConfig().getComponent("global.disabled")); - return; - } - - // Конвертируем Component в String для обработки упоминаний - String messageText = PLAIN.serialize(message); - - // Обработка упоминаний @ник - Set mentionedPlayers = new HashSet<>(); - String processedMessage = mentionService.processMentions(messageText, mentionedPlayers); - - String format = plugin.getConfigManager().getGlobalFormat(); - String prefix = plugin.getConfigManager().getGlobalPrefix(); - - // Получаем отображаемое имя игрока (с градиентом если есть) - String playerDisplay = getPlayerDisplay(sender); - - // Отправка каждому игроку персонализированное сообщение - for (Player player : Bukkit.getOnlinePlayers()) { - if (isGlobalChatDisabled(player.getUniqueId())) continue; - - // Персонализируем сообщение для этого игрока (выделяем его ник) - String personalizedMsg = mentionService.getPersonalizedMessage(processedMessage, player); - - // Формируем итоговую строку - String formatted = format - .replace("", prefix) - .replace("", playerDisplay) - .replace("", personalizedMsg); - - // Парсим в Component и отправляем - Component component = MINI_MESSAGE.deserialize(formatted); - player.sendMessage(component); - - // Проверяем упоминание ника без @ и уведомляем - mentionService.notifyIfMentioned(messageText, player, sender); - } - - // Уведомление упомянутых через @ - mentionService.notifyMentioned(mentionedPlayers); - - plugin.getLogger().info("[G] " + sender.getName() + ": " + ChatFormatter.stripTags(messageText)); - } - - public void sendLocalMessage(Player sender, Component message) { - if (!plugin.getConfigManager().isLocalEnabled()) { - return; - } - - // Конвертируем Component в String для обработки упоминаний - String messageText = PLAIN.serialize(message); - - // Обработка упоминаний @ник - Set mentionedPlayers = new HashSet<>(); - String processedMessage = mentionService.processMentions(messageText, mentionedPlayers); - - int radius = plugin.getConfigManager().getLocalRadius(); - String format = plugin.getConfigManager().getLocalFormat(); - String prefix = plugin.getConfigManager().getLocalPrefix(); - - // Получаем отображаемое имя игрока (с градиентом если есть) - String playerDisplay = getPlayerDisplay(sender); - - boolean foundRecipient = false; - Set recipients = new HashSet<>(); - - for (Player player : Bukkit.getOnlinePlayers()) { - if (DistanceUtil.isInRange(sender, player, radius)) { - // Персонализируем сообщение для этого игрока - String personalizedMsg = mentionService.getPersonalizedMessage(processedMessage, player); - - // Формируем итоговую строку - String formatted = prefix + " " + format - .replace("", playerDisplay) - .replace("", personalizedMsg); - - // Парсим в Component и отправляем - Component component = MINI_MESSAGE.deserialize(formatted); - player.sendMessage(component); - - recipients.add(player); - - // Проверяем упоминание ника без @ - mentionService.notifyIfMentioned(messageText, player, sender); - - if (!player.equals(sender)) { - foundRecipient = true; - } - } - } - - // Уведомление упомянутых через @ (только тех кто в радиусе) - mentionedPlayers.retainAll(recipients); - mentionService.notifyMentioned(mentionedPlayers); - - if (!foundRecipient) { - sender.sendMessage(plugin.getMessageConfig().getComponent("local.no-players")); - } - } - - /** - * Получает отображаемое имя игрока с градиентом (если включено) - * Возвращает строку в MiniMessage формате - */ - private String getPlayerDisplay(Player player) { - if (plugin.getGradientModule() != null && plugin.getGradientModule().isEnabled()) { - String name = plugin.getGradientModule().getFormattedName(player); - // Конвертируем &#RRGGBB формат в MiniMessage - return ChatFormatter.convertAllColors(name); - } - return player.getName(); - } - - public boolean toggleGlobalChat(UUID player) { - if (globalChatDisabled.contains(player)) { - globalChatDisabled.remove(player); - return true; - } else { - globalChatDisabled.add(player); - return false; - } - } - - public boolean isGlobalChatDisabled(UUID player) { - return globalChatDisabled.contains(player); - } -} diff --git a/src/main/java/com/loki/lochat/managers/CooldownManager.java b/src/main/java/com/loki/lochat/managers/CooldownManager.java deleted file mode 100644 index 4f111d5..0000000 --- a/src/main/java/com/loki/lochat/managers/CooldownManager.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.loki.lochat.managers; - -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -public class CooldownManager { - - private final Map globalCooldowns = new ConcurrentHashMap<>(); - private final Map localCooldowns = new ConcurrentHashMap<>(); - - public boolean isOnCooldown(UUID player, String chatType, int cooldownSeconds) { - Map cooldowns = chatType.equals("global") ? globalCooldowns : localCooldowns; - Long lastTime = cooldowns.get(player); - - if (lastTime == null) { - return false; - } - - long elapsed = (System.currentTimeMillis() - lastTime) / 1000; - return elapsed < cooldownSeconds; - } - - public int getRemainingCooldown(UUID player, String chatType, int cooldownSeconds) { - Map cooldowns = chatType.equals("global") ? globalCooldowns : localCooldowns; - Long lastTime = cooldowns.get(player); - - if (lastTime == null) { - return 0; - } - - long elapsed = (System.currentTimeMillis() - lastTime) / 1000; - return Math.max(0, cooldownSeconds - (int) elapsed); - } - - public void setCooldown(UUID player, String chatType) { - Map cooldowns = chatType.equals("global") ? globalCooldowns : localCooldowns; - cooldowns.put(player, System.currentTimeMillis()); - } - - public void removeCooldown(UUID player) { - globalCooldowns.remove(player); - localCooldowns.remove(player); - } -} diff --git a/src/main/java/com/loki/lochat/managers/IgnoreManager.java b/src/main/java/com/loki/lochat/managers/IgnoreManager.java deleted file mode 100644 index 90d2ab8..0000000 --- a/src/main/java/com/loki/lochat/managers/IgnoreManager.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.loki.lochat.managers; - -import com.loki.lochat.LoChat; -import com.loki.lochat.gradient.util.FoliaUtil; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -public class IgnoreManager { - - private final LoChat plugin; - private final File ignoreFile; - private final Map> ignoreMap = new ConcurrentHashMap<>(); - - public IgnoreManager(LoChat plugin) { - this.plugin = plugin; - this.ignoreFile = new File(plugin.getDataFolder(), "ignores.yml"); - load(); - } - - public void load() { - if (!ignoreFile.exists()) return; - - YamlConfiguration config = YamlConfiguration.loadConfiguration(ignoreFile); - for (String key : config.getKeys(false)) { - try { - UUID uuid = UUID.fromString(key); - List ignoredList = config.getStringList(key); - Set ignored = ConcurrentHashMap.newKeySet(); - - for (String ignoredStr : ignoredList) { - try { - ignored.add(UUID.fromString(ignoredStr)); - } catch (Exception ignored2) {} - } - - if (!ignored.isEmpty()) { - ignoreMap.put(uuid, ignored); - } - } catch (Exception ignored) {} - } - } - - public void save() { - YamlConfiguration config = new YamlConfiguration(); - - for (Map.Entry> entry : ignoreMap.entrySet()) { - List ignoredList = new ArrayList<>(); - for (UUID ignored : entry.getValue()) { - ignoredList.add(ignored.toString()); - } - config.set(entry.getKey().toString(), ignoredList); - } - - try { - config.save(ignoreFile); - } catch (IOException e) { - plugin.getLogger().severe("Не удалось сохранить игноры: " + e.getMessage()); - } - } - - public boolean isIgnoring(UUID player, UUID target) { - Set ignored = ignoreMap.get(player); - return ignored != null && ignored.contains(target); - } - - public boolean addIgnore(UUID player, UUID target) { - Set ignored = ignoreMap.computeIfAbsent(player, k -> ConcurrentHashMap.newKeySet()); - boolean added = ignored.add(target); - if (added) saveAsync(); - return added; - } - - public boolean removeIgnore(UUID player, UUID target) { - Set ignored = ignoreMap.get(player); - if (ignored == null) return false; - boolean removed = ignored.remove(target); - if (removed) saveAsync(); - return removed; - } - - public Set getIgnoredPlayers(UUID player) { - return ignoreMap.getOrDefault(player, Collections.emptySet()); - } - - public int getIgnoredCount(UUID player) { - Set ignored = ignoreMap.get(player); - return ignored == null ? 0 : ignored.size(); - } - - public void clearIgnores(UUID player) { - ignoreMap.remove(player); - saveAsync(); - } - - /** - * Асинхронное сохранение данных (для Folia совместимости) - */ - private void saveAsync() { - FoliaUtil.runAsync(plugin, this::save); - } -} diff --git a/src/main/java/com/loki/lochat/managers/MentionManager.java b/src/main/java/com/loki/lochat/managers/MentionManager.java deleted file mode 100644 index 8199292..0000000 --- a/src/main/java/com/loki/lochat/managers/MentionManager.java +++ /dev/null @@ -1,141 +0,0 @@ -package com.loki.lochat.managers; - -import com.loki.lochat.LoChat; -import org.bukkit.Bukkit; -import org.bukkit.Sound; -import org.bukkit.entity.Player; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class MentionManager { - - private final LoChat plugin; - private static final Pattern MENTION_PATTERN = Pattern.compile("@(\\w+)"); - - // Хранит для какого игрока какие части сообщения выделить - private final Map> highlightedNicks = new HashMap<>(); - - public MentionManager(LoChat plugin) { - this.plugin = plugin; - } - - /** - * Обрабатывает упоминания в сообщении - * Возвращает сообщение с подсвеченными никами - */ - public String processMentions(String message, Set mentionedPlayers) { - if (!plugin.getConfigManager().isMentionsEnabled()) { - return message; - } - - highlightedNicks.clear(); - String highlight = plugin.getConfigManager().getMentionHighlight(); - Matcher matcher = MENTION_PATTERN.matcher(message); - StringBuilder result = new StringBuilder(); - - while (matcher.find()) { - String name = matcher.group(1); - Player mentioned = Bukkit.getPlayer(name); - - if (mentioned != null && mentioned.isOnline()) { - mentionedPlayers.add(mentioned); - String replacement = highlight.replace("{player}", mentioned.getName()); - matcher.appendReplacement(result, replacement); - - // Запоминаем что этот игрок упомянут - highlightedNicks.computeIfAbsent(mentioned, k -> new HashSet<>()).add(mentioned.getName()); - } - } - matcher.appendTail(result); - - return result.toString(); - } - - /** - * Создаёт персонализированное сообщение для игрока - * Если его ник упомянут — выделяет его особым образом - */ - public String getPersonalizedMessage(String message, Player viewer) { - if (!plugin.getConfigManager().isMentionsEnabled()) { - return message; - } - - String viewerName = viewer.getName(); - String result = message; - - // Проверяем упоминание через @ник - String highlight = plugin.getConfigManager().getMentionHighlight(); - String highlightedName = highlight.replace("{player}", viewerName); - - // Если в сообщении есть выделенный ник этого игрока — добавляем дополнительное выделение - if (result.contains(highlightedName)) { - // Уже выделено через processMentions, добавляем эффект - String extraHighlight = "" + highlightedName + ""; - result = result.replace(highlightedName, extraHighlight); - } - - // Также проверяем просто ник в сообщении (без @) - // Регистронезависимый поиск - Pattern nickPattern = Pattern.compile("(?i)\\b" + Pattern.quote(viewerName) + "\\b"); - Matcher nickMatcher = nickPattern.matcher(result); - - if (nickMatcher.find()) { - // Ник найден в сообщении — выделяем для этого игрока - String selfHighlight = plugin.getConfigManager().getSelfMentionHighlight(); - result = nickMatcher.replaceAll(selfHighlight.replace("{player}", viewerName)); - } - - return result; - } - - /** - * Проверяет, упомянут ли игрок в сообщении (по нику без @) - */ - public boolean isPlayerMentioned(String message, Player player) { - String name = player.getName(); - Pattern pattern = Pattern.compile("(?i)\\b" + Pattern.quote(name) + "\\b"); - return pattern.matcher(message).find(); - } - - /** - * Уведомляет упомянутых игроков звуком - */ - public void notifyMentioned(Set players) { - if (!plugin.getConfigManager().isMentionSoundEnabled()) { - return; - } - - String soundName = plugin.getConfigManager().getMentionSoundType(); - Sound sound; - try { - sound = Sound.valueOf(soundName); - } catch (IllegalArgumentException e) { - sound = Sound.BLOCK_NOTE_BLOCK_PLING; - } - - for (Player player : players) { - player.playSound(player.getLocation(), sound, 1.0f, 1.0f); - } - } - - /** - * Уведомляет игрока если его ник упомянут (без @) - */ - public void notifyIfMentioned(String message, Player viewer, Player sender) { - if (viewer.equals(sender)) return; - if (!plugin.getConfigManager().isMentionSoundEnabled()) return; - - if (isPlayerMentioned(message, viewer)) { - String soundName = plugin.getConfigManager().getMentionSoundType(); - try { - Sound sound = Sound.valueOf(soundName); - viewer.playSound(viewer.getLocation(), sound, 1.0f, 1.0f); - } catch (IllegalArgumentException ignored) {} - } - } -} diff --git a/src/main/java/com/loki/lochat/managers/MuteManager.java b/src/main/java/com/loki/lochat/managers/MuteManager.java deleted file mode 100644 index e22fdc5..0000000 --- a/src/main/java/com/loki/lochat/managers/MuteManager.java +++ /dev/null @@ -1,447 +0,0 @@ -package com.loki.lochat.managers; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; -import com.loki.lochat.LoChat; -import com.loki.lochat.gradient.util.FoliaUtil; - -import java.io.*; -import java.lang.reflect.Type; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Менеджер мутов для LoChat - */ -public class MuteManager { - - private final LoChat plugin; - private final Map mutes = new ConcurrentHashMap<>(); - private final Map> history = new ConcurrentHashMap<>(); - private final File dataFile; - private final File historyFile; - private final Gson gson = new GsonBuilder().setPrettyPrinting().create(); - - public MuteManager(LoChat plugin) { - this.plugin = plugin; - this.dataFile = new File(plugin.getDataFolder(), "mutes.json"); - this.historyFile = new File(plugin.getDataFolder(), "mute-history.json"); - load(); - loadHistory(); - } - - /** - * Замутить игрока - * @param uuid UUID игрока - * @param playerName имя игрока - * @param duration длительность в миллисекундах (0 = перманентный) - * @param reason причина - * @param mutedBy кто замутил - */ - public void mute(UUID uuid, String playerName, long duration, String reason, String mutedBy) { - long endTime = duration > 0 ? System.currentTimeMillis() + duration : 0; - MuteData data = new MuteData(endTime, reason, mutedBy, System.currentTimeMillis(), playerName); - mutes.put(uuid, data); - - // Добавляем в историю - addToHistory(uuid, playerName, duration, reason, mutedBy); - - saveAsync(); - } - - /** - * Замутить игрока (без имени - для совместимости) - */ - public void mute(UUID uuid, long duration, String reason, String mutedBy) { - mute(uuid, null, duration, reason, mutedBy); - } - - /** - * Размутить игрока - * @param uuid UUID игрока - * @param unmutedBy кто размутил (null если автоматически) - * @return true если игрок был замучен - */ - public boolean unmute(UUID uuid, String unmutedBy) { - MuteData removed = mutes.remove(uuid); - if (removed != null) { - // Обновляем историю - помечаем как размученный - updateHistoryUnmute(uuid, unmutedBy); - saveAsync(); - return true; - } - return false; - } - - /** - * Размутить игрока (для совместимости) - */ - public boolean unmute(UUID uuid) { - return unmute(uuid, null); - } - - /** - * Проверить, замучен ли игрок - */ - public boolean isMuted(UUID uuid) { - MuteData data = mutes.get(uuid); - if (data == null) return false; - - // Проверяем истёк ли мут - if (data.endTime > 0 && System.currentTimeMillis() > data.endTime) { - mutes.remove(uuid); - saveAsync(); - return false; - } - return true; - } - - /** - * Получить данные мута - */ - public MuteData getMuteData(UUID uuid) { - if (!isMuted(uuid)) return null; - return mutes.get(uuid); - } - - /** - * Получить оставшееся время мута - */ - public long getRemainingTime(UUID uuid) { - MuteData data = mutes.get(uuid); - if (data == null) return 0; - if (data.endTime == 0) return -1; // Перманентный - return Math.max(0, data.endTime - System.currentTimeMillis()); - } - - /** - * Форматировать время - */ - public String formatTime(long millis) { - if (millis <= 0) return "0с"; - - long seconds = millis / 1000; - long minutes = seconds / 60; - long hours = minutes / 60; - long days = hours / 24; - - if (days > 0) { - return days + "д " + (hours % 24) + "ч"; - } else if (hours > 0) { - return hours + "ч " + (minutes % 60) + "м"; - } else if (minutes > 0) { - return minutes + "м " + (seconds % 60) + "с"; - } else { - return seconds + "с"; - } - } - - /** - * Парсинг времени из строки (1d, 2h, 30m, 60s) - */ - public long parseTime(String timeStr) { - if (timeStr == null || timeStr.isEmpty()) return 0; - - timeStr = timeStr.toLowerCase(); - long multiplier = 1000; // базово в миллисекундах - - try { - if (timeStr.endsWith("d")) { - return Long.parseLong(timeStr.replace("d", "")) * 24 * 60 * 60 * multiplier; - } else if (timeStr.endsWith("h")) { - return Long.parseLong(timeStr.replace("h", "")) * 60 * 60 * multiplier; - } else if (timeStr.endsWith("m")) { - return Long.parseLong(timeStr.replace("m", "")) * 60 * multiplier; - } else if (timeStr.endsWith("s")) { - return Long.parseLong(timeStr.replace("s", "")) * multiplier; - } else { - // Если без суффикса - считаем минутами - return Long.parseLong(timeStr) * 60 * multiplier; - } - } catch (NumberFormatException e) { - return -1; // Ошибка парсинга - } - } - - private void load() { - if (!dataFile.exists()) return; - - try (Reader reader = new FileReader(dataFile)) { - Type type = new TypeToken>(){}.getType(); - Map loaded = gson.fromJson(reader, type); - if (loaded != null) { - loaded.forEach((key, value) -> { - try { - mutes.put(UUID.fromString(key), value); - } catch (IllegalArgumentException ignored) {} - }); - } - } catch (IOException e) { - plugin.getLogger().warning("Ошибка загрузки мутов: " + e.getMessage()); - } - } - - public void save() { - try { - if (!dataFile.getParentFile().exists()) { - dataFile.getParentFile().mkdirs(); - } - - Map toSave = new ConcurrentHashMap<>(); - mutes.forEach((uuid, data) -> toSave.put(uuid.toString(), data)); - - try (Writer writer = new FileWriter(dataFile)) { - gson.toJson(toSave, writer); - } - } catch (IOException e) { - plugin.getLogger().warning("Ошибка сохранения мутов: " + e.getMessage()); - } - } - - private void loadHistory() { - if (!historyFile.exists()) return; - - try (Reader reader = new FileReader(historyFile)) { - Type type = new TypeToken>>(){}.getType(); - Map> loaded = gson.fromJson(reader, type); - if (loaded != null) { - loaded.forEach((key, value) -> { - try { - history.put(UUID.fromString(key), new ArrayList<>(value)); - } catch (IllegalArgumentException ignored) {} - }); - } - } catch (IOException e) { - plugin.getLogger().warning("Ошибка загрузки истории мутов: " + e.getMessage()); - } - } - - public void saveHistory() { - try { - if (!historyFile.getParentFile().exists()) { - historyFile.getParentFile().mkdirs(); - } - - Map> toSave = new ConcurrentHashMap<>(); - history.forEach((uuid, data) -> toSave.put(uuid.toString(), data)); - - try (Writer writer = new FileWriter(historyFile)) { - gson.toJson(toSave, writer); - } - } catch (IOException e) { - plugin.getLogger().warning("Ошибка сохранения истории мутов: " + e.getMessage()); - } - } - - private void saveHistoryAsync() { - FoliaUtil.runAsync(plugin, this::saveHistory); - } - - private void saveAsync() { - FoliaUtil.runAsync(plugin, () -> { - save(); - saveHistory(); - }); - } - - /** - * Добавить запись в историю - */ - private void addToHistory(UUID uuid, String playerName, long duration, String reason, String mutedBy) { - List playerHistory = history.computeIfAbsent(uuid, k -> new ArrayList<>()); - playerHistory.add(new MuteHistoryEntry( - playerName, - duration, - reason, - mutedBy, - System.currentTimeMillis(), - false, - null, - 0 - )); - saveHistoryAsync(); - } - - /** - * Обновить историю при размуте - */ - private void updateHistoryUnmute(UUID uuid, String unmutedBy) { - List playerHistory = history.get(uuid); - if (playerHistory != null && !playerHistory.isEmpty()) { - // Находим последний активный мут - for (int i = playerHistory.size() - 1; i >= 0; i--) { - MuteHistoryEntry entry = playerHistory.get(i); - if (!entry.unmuted) { - entry.unmuted = true; - entry.unmutedBy = unmutedBy; - entry.unmutedAt = System.currentTimeMillis(); - break; - } - } - } - saveHistoryAsync(); - } - - /** - * Получить список всех активных мутов - */ - public Map getActiveMutes() { - // Очищаем истёкшие муты - mutes.entrySet().removeIf(entry -> { - MuteData data = entry.getValue(); - return data.endTime > 0 && System.currentTimeMillis() > data.endTime; - }); - return new HashMap<>(mutes); - } - - /** - * Получить историю мутов игрока - */ - public List getPlayerHistory(UUID uuid) { - return history.getOrDefault(uuid, new ArrayList<>()); - } - - /** - * Получить UUID по имени из истории - */ - public UUID getUUIDByName(String name) { - for (Map.Entry> entry : history.entrySet()) { - for (MuteHistoryEntry historyEntry : entry.getValue()) { - if (historyEntry.playerName != null && historyEntry.playerName.equalsIgnoreCase(name)) { - return entry.getKey(); - } - } - } - return null; - } - - /** - * Получить историю мутов выданных модератором - */ - public List getMutesByOperator(String operatorName) { - List result = new ArrayList<>(); - for (List playerHistory : history.values()) { - for (MuteHistoryEntry entry : playerHistory) { - if (entry.mutedBy != null && entry.mutedBy.equalsIgnoreCase(operatorName)) { - result.add(entry); - } - } - } - // Сортируем по дате (новые первые) - result.sort((a, b) -> Long.compare(b.mutedAt, a.mutedAt)); - return result; - } - - /** - * Получить максимальную доступную длительность для игрока - * @param player игрок (модератор) - * @return длительность в миллисекундах, 0 = перманентный, -1 = нет прав - */ - public long getMaxDuration(org.bukkit.entity.Player player) { - if (player.hasPermission("lochat.mute.dur.perm")) { - return 0; // Перманентный - } - - // Проверяем права от большего к меньшему - String[] durations = {"30d", "14d", "7d", "3d", "1d", "12h", "6h", "1h", "30m", "10m"}; - for (String dur : durations) { - if (player.hasPermission("lochat.mute.dur." + dur)) { - return parseTime(dur); - } - } - - return -1; // Нет прав - } - - /** - * Проверить, может ли игрок выдать мут на указанную длительность - */ - public boolean canMuteForDuration(org.bukkit.entity.Player player, long duration) { - if (player.hasPermission("lochat.mute.dur.perm")) { - return true; // Может всё - } - - if (duration == 0) { - return false; // Перманентный мут требует lochat.mute.dur.perm - } - - long maxDuration = getMaxDuration(player); - if (maxDuration == -1) return false; - if (maxDuration == 0) return true; // Перманентный = может всё - - return duration <= maxDuration; - } - - /** - * Форматировать сообщение с плейсхолдерами - */ - public String formatMessage(String message, String player, String operator, String duration, String reason) { - return message - .replace("%player%", player != null ? player : "") - .replace("%operator%", operator != null ? operator : "") - .replace("%duration%", duration != null ? duration : "") - .replace("%reason%", reason != null ? reason : ""); - } - - /** - * Данные о муте - */ - public static class MuteData { - public long endTime; // 0 = перманентный - public String reason; - public String mutedBy; - public long mutedAt; - public String playerName; - - public MuteData() {} - - public MuteData(long endTime, String reason, String mutedBy, long mutedAt) { - this(endTime, reason, mutedBy, mutedAt, null); - } - - public MuteData(long endTime, String reason, String mutedBy, long mutedAt, String playerName) { - this.endTime = endTime; - this.reason = reason; - this.mutedBy = mutedBy; - this.mutedAt = mutedAt; - this.playerName = playerName; - } - - public boolean isPermanent() { - return endTime == 0; - } - } - - /** - * Запись истории мута - */ - public static class MuteHistoryEntry { - public String playerName; - public long duration; // 0 = перманентный - public String reason; - public String mutedBy; - public long mutedAt; - public boolean unmuted; - public String unmutedBy; - public long unmutedAt; - - public MuteHistoryEntry() {} - - public MuteHistoryEntry(String playerName, long duration, String reason, String mutedBy, - long mutedAt, boolean unmuted, String unmutedBy, long unmutedAt) { - this.playerName = playerName; - this.duration = duration; - this.reason = reason; - this.mutedBy = mutedBy; - this.mutedAt = mutedAt; - this.unmuted = unmuted; - this.unmutedBy = unmutedBy; - this.unmutedAt = unmutedAt; - } - - public boolean isPermanent() { - return duration == 0; - } - } -} diff --git a/src/main/java/com/loki/lochat/managers/PMManager.java b/src/main/java/com/loki/lochat/managers/PMManager.java deleted file mode 100644 index 61105d6..0000000 --- a/src/main/java/com/loki/lochat/managers/PMManager.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.loki.lochat.managers; - -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -public class PMManager { - - // UUID игрока -> UUID последнего собеседника - private final Map lastConversation = new ConcurrentHashMap<>(); - - public void setLastConversation(UUID player, UUID target) { - lastConversation.put(player, target); - } - - public UUID getLastConversation(UUID player) { - return lastConversation.get(player); - } - - public void removeConversation(UUID player) { - lastConversation.remove(player); - } - - public boolean hasConversation(UUID player) { - return lastConversation.containsKey(player); - } -} diff --git a/src/main/java/com/loki/lochat/managers/SpyManager.java b/src/main/java/com/loki/lochat/managers/SpyManager.java deleted file mode 100644 index 5607a57..0000000 --- a/src/main/java/com/loki/lochat/managers/SpyManager.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.loki.lochat.managers; - -import com.loki.lochat.LoChat; -import com.loki.lochat.utils.ChatFormatter; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -public class SpyManager { - - private final LoChat plugin; - private final Set spyEnabled = ConcurrentHashMap.newKeySet(); - - public SpyManager(LoChat plugin) { - this.plugin = plugin; - } - - public boolean toggleSpy(UUID player) { - if (spyEnabled.contains(player)) { - spyEnabled.remove(player); - return false; - } else { - spyEnabled.add(player); - return true; - } - } - - public boolean isSpying(UUID player) { - return spyEnabled.contains(player); - } - - public void broadcastPM(Player sender, Player receiver, String message) { - String format = plugin.getMessageConfig().get("spy.format"); - - for (UUID spyUuid : spyEnabled) { - Player spy = Bukkit.getPlayer(spyUuid); - if (spy != null && spy.isOnline() && !spy.equals(sender) && !spy.equals(receiver)) { - spy.sendMessage(ChatFormatter.parse(format - .replace("{sender}", sender.getName()) - .replace("{receiver}", receiver.getName()) - .replace("{message}", message))); - } - } - } - - public void sendToSpies(Player sender, net.kyori.adventure.text.Component message, boolean isGlobal) { - if (spyEnabled.isEmpty()) return; - - String chatType = isGlobal ? "Global" : "Local"; - String plainMessage = net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer.plainText().serialize(message); - String format = plugin.getMessageConfig().get("spy.chat-format", - "§7[SPY] §e{type} §7{sender}: §f{message}"); - - for (UUID spyUuid : spyEnabled) { - Player spy = Bukkit.getPlayer(spyUuid); - if (spy != null && spy.isOnline() && !spy.equals(sender)) { - spy.sendMessage(ChatFormatter.parse(format - .replace("{type}", chatType) - .replace("{sender}", sender.getName()) - .replace("{message}", plainMessage))); - } - } - } - - public void removeSpy(UUID player) { - spyEnabled.remove(player); - } -} diff --git a/src/main/java/com/loki/lochat/utils/AdventureManager.java b/src/main/java/com/loki/lochat/utils/AdventureManager.java new file mode 100644 index 0000000..69a05ec --- /dev/null +++ b/src/main/java/com/loki/lochat/utils/AdventureManager.java @@ -0,0 +1,88 @@ +package com.loki.lochat.utils; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +/** + * Менеджер для работы с Adventure API на Arclight/Forge серверах + */ +public class AdventureManager { + + private static boolean adventureAvailable = false; + private static Object audiences; + + public static void init(Plugin plugin) { + try { + // Проверяем наличие Adventure API + Class.forName("net.kyori.adventure.text.Component"); + Class audiencesClass = Class.forName("net.kyori.adventure.platform.bukkit.BukkitAudiences"); + + // Создаем BukkitAudiences через рефлексию + Object audiencesInstance = audiencesClass.getMethod("create", Plugin.class).invoke(null, plugin); + audiences = audiencesInstance; + adventureAvailable = true; + + plugin.getLogger().info("Adventure API обнаружен - используем нативную поддержку"); + } catch (Exception e) { + adventureAvailable = false; + plugin.getLogger().info("Adventure API не найден - используем legacy режим"); + } + } + + public static void shutdown() { + if (audiences != null && adventureAvailable) { + try { + audiences.getClass().getMethod("close").invoke(audiences); + } catch (Exception e) { + // Игнорируем ошибки при закрытии + } + audiences = null; + } + adventureAvailable = false; + } + + public static void sendMessage(CommandSender sender, Object message) { + if (adventureAvailable && audiences != null) { + try { + // Используем Adventure API + Object senderAudience = audiences.getClass().getMethod("sender", CommandSender.class).invoke(audiences, sender); + senderAudience.getClass().getMethod("sendMessage", Class.forName("net.kyori.adventure.text.Component")).invoke(senderAudience, message); + return; + } catch (Exception e) { + // Fallback на legacy + } + } + + // Legacy fallback + if (message instanceof String) { + sender.sendMessage((String) message); + } else { + sender.sendMessage(message.toString()); + } + } + + public static void sendMessage(Player player, Object message) { + if (adventureAvailable && audiences != null) { + try { + // Используем Adventure API + Object playerAudience = audiences.getClass().getMethod("player", Player.class).invoke(audiences, player); + playerAudience.getClass().getMethod("sendMessage", Class.forName("net.kyori.adventure.text.Component")).invoke(playerAudience, message); + return; + } catch (Exception e) { + // Fallback на legacy + } + } + + // Legacy fallback + if (message instanceof String) { + player.sendMessage((String) message); + } else { + player.sendMessage(message.toString()); + } + } + + public static boolean isAdventureAvailable() { + return adventureAvailable; + } +} \ No newline at end of file diff --git a/src/test/java/com/loki/lochat/managers/CooldownManagerTest.java b/src/test/java/com/loki/lochat/managers/CooldownManagerTest.java deleted file mode 100644 index 01970ba..0000000 --- a/src/test/java/com/loki/lochat/managers/CooldownManagerTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.loki.lochat.managers; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.UUID; - -import static org.junit.jupiter.api.Assertions.*; - -class CooldownManagerTest { - - private CooldownManager cooldownManager; - private UUID testPlayer; - - @BeforeEach - void setUp() { - cooldownManager = new CooldownManager(); - testPlayer = UUID.randomUUID(); - } - - @Test - void isOnCooldown_noCooldown() { - assertFalse(cooldownManager.isOnCooldown(testPlayer, "global", 5)); - } - - @Test - void isOnCooldown_afterSet() { - cooldownManager.setCooldown(testPlayer, "global"); - assertTrue(cooldownManager.isOnCooldown(testPlayer, "global", 5)); - } - - @Test - void getRemainingCooldown_noCooldown() { - assertEquals(0, cooldownManager.getRemainingCooldown(testPlayer, "global", 5)); - } - - @Test - void removeCooldown() { - cooldownManager.setCooldown(testPlayer, "global"); - cooldownManager.removeCooldown(testPlayer); - assertFalse(cooldownManager.isOnCooldown(testPlayer, "global", 5)); - } - - @Test - void separateCooldowns() { - cooldownManager.setCooldown(testPlayer, "global"); - assertTrue(cooldownManager.isOnCooldown(testPlayer, "global", 5)); - assertFalse(cooldownManager.isOnCooldown(testPlayer, "local", 5)); - } -} diff --git a/src/test/java/com/loki/lochat/managers/PMManagerTest.java b/src/test/java/com/loki/lochat/managers/PMManagerTest.java deleted file mode 100644 index 9c9c2ea..0000000 --- a/src/test/java/com/loki/lochat/managers/PMManagerTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.loki.lochat.managers; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.UUID; - -import static org.junit.jupiter.api.Assertions.*; - -class PMManagerTest { - - private PMManager pmManager; - private UUID player1; - private UUID player2; - - @BeforeEach - void setUp() { - pmManager = new PMManager(); - player1 = UUID.randomUUID(); - player2 = UUID.randomUUID(); - } - - @Test - void setAndGetLastConversation() { - pmManager.setLastConversation(player1, player2); - assertEquals(player2, pmManager.getLastConversation(player1)); - } - - @Test - void getLastConversation_noConversation() { - assertNull(pmManager.getLastConversation(player1)); - } - - @Test - void hasConversation() { - assertFalse(pmManager.hasConversation(player1)); - pmManager.setLastConversation(player1, player2); - assertTrue(pmManager.hasConversation(player1)); - } - - @Test - void removeConversation() { - pmManager.setLastConversation(player1, player2); - pmManager.removeConversation(player1); - assertFalse(pmManager.hasConversation(player1)); - } -}