From 5082e6ff6ab3ff93c1b301ffdbef6cb7e480a764 Mon Sep 17 00:00:00 2001 From: wolf Date: Fri, 21 Mar 2025 15:43:52 -0500 Subject: [PATCH] More item checks, new item storage. --- .gradle/8.5/checksums/checksums.lock | Bin 17 -> 17 bytes .gradle/8.5/checksums/md5-checksums.bin | Bin 41097 -> 41147 bytes .gradle/8.5/checksums/sha1-checksums.bin | Bin 171089 -> 171413 bytes .../8.5/executionHistory/executionHistory.bin | Bin 2344489 -> 2344489 bytes .../executionHistory/executionHistory.lock | Bin 17 -> 17 bytes .gradle/8.5/fileHashes/fileHashes.bin | Bin 288319 -> 288969 bytes .gradle/8.5/fileHashes/fileHashes.lock | Bin 17 -> 17 bytes .../8.5/fileHashes/resourceHashesCache.bin | Bin 206151 -> 206695 bytes .../buildOutputCleanup.lock | Bin 17 -> 17 bytes .gradle/file-system.probe | Bin 8 -> 8 bytes .../sentinel/data/config/NBTConfig.java | 1 + .../sentinel/data/storage/NBTStorage.java | 86 +++++-- .../violations/players/CreativeHotbar.java | 8 +- .../functions/hotbar/AbstractCheck.java | 9 + .../entities}/EntityCheck.java | 10 +- .../entities}/EntitySnapshotCheck.java | 3 +- .../entities}/EquipmentCheck.java | 8 +- .../items}/EnchantmentCheck.java | 12 +- .../functions/hotbar/items/ItemCheck.java | 81 +++++++ .../functions/hotbar/items/MetaCheck.java | 65 ++++++ .../items}/RateLimitCheck.java | 24 +- .../functions/hotbar/items/SpawnEggCheck.java | 50 ++++ .../hotbar/misc/BlockStateCheck.java | 100 ++++++++ .../misc}/InventoryCheck.java | 25 +- .../functions/hotbar/nbt/ComponentCheck.java | 32 +++ .../functions/hotbar/nbt/EntityDataCheck.java | 40 ++++ .../functions/itemchecks/AbstractCheck.java | 10 - .../functions/itemchecks/ItemCheck.java | 218 ------------------ .../functions/itemchecks/SpawnEggCheck.java | 31 --- .../itemchecks/TrialSpawnerCheck.java | 33 --- .../trouper/sentinel/startup/drm/Loader.java | 7 +- 31 files changed, 496 insertions(+), 357 deletions(-) create mode 100644 src/main/java/me/trouper/sentinel/server/functions/hotbar/AbstractCheck.java rename src/main/java/me/trouper/sentinel/server/functions/{itemchecks => hotbar/entities}/EntityCheck.java (87%) rename src/main/java/me/trouper/sentinel/server/functions/{itemchecks => hotbar/entities}/EntitySnapshotCheck.java (83%) rename src/main/java/me/trouper/sentinel/server/functions/{itemchecks => hotbar/entities}/EquipmentCheck.java (57%) rename src/main/java/me/trouper/sentinel/server/functions/{itemchecks => hotbar/items}/EnchantmentCheck.java (96%) create mode 100644 src/main/java/me/trouper/sentinel/server/functions/hotbar/items/ItemCheck.java create mode 100644 src/main/java/me/trouper/sentinel/server/functions/hotbar/items/MetaCheck.java rename src/main/java/me/trouper/sentinel/server/functions/{itemchecks => hotbar/items}/RateLimitCheck.java (73%) create mode 100644 src/main/java/me/trouper/sentinel/server/functions/hotbar/items/SpawnEggCheck.java create mode 100644 src/main/java/me/trouper/sentinel/server/functions/hotbar/misc/BlockStateCheck.java rename src/main/java/me/trouper/sentinel/server/functions/{itemchecks => hotbar/misc}/InventoryCheck.java (56%) create mode 100644 src/main/java/me/trouper/sentinel/server/functions/hotbar/nbt/ComponentCheck.java create mode 100644 src/main/java/me/trouper/sentinel/server/functions/hotbar/nbt/EntityDataCheck.java delete mode 100644 src/main/java/me/trouper/sentinel/server/functions/itemchecks/AbstractCheck.java delete mode 100644 src/main/java/me/trouper/sentinel/server/functions/itemchecks/ItemCheck.java delete mode 100644 src/main/java/me/trouper/sentinel/server/functions/itemchecks/SpawnEggCheck.java delete mode 100644 src/main/java/me/trouper/sentinel/server/functions/itemchecks/TrialSpawnerCheck.java diff --git a/.gradle/8.5/checksums/checksums.lock b/.gradle/8.5/checksums/checksums.lock index 588ab510878fda9d9be7630b90b1400e9c6c6b1e..7c1a560ffb59aee6ed0ff5e042d54a55b2164497 100644 GIT binary patch literal 17 VcmZSfyWC&;{v_{G1~A}>0{}VT1pNR2 literal 17 VcmZSfyWC&;{v_{G1~B091OPe91n2+& diff --git a/.gradle/8.5/checksums/md5-checksums.bin b/.gradle/8.5/checksums/md5-checksums.bin index ddf14d4bb024f7e6fd1a81d7ed9c8ab8e131e509..5268b4d7a7e7ac485c9bf6c275130af124cb7e18 100644 GIT binary patch delta 100 zcmeA?$h7+)(*_d>#-p1}B@9*gv(iF(mprXw0D}dcn`3R9*%&)FDt54LcJ%mf&pPG* yUO&ant{pBCldY%lsrW8iyzHW#c#v_|eB@9(I*V$OHF&1uAT)?{7(c`~8>r`%`V4cmb9WD|8UE~h~ diff --git a/.gradle/8.5/checksums/sha1-checksums.bin b/.gradle/8.5/checksums/sha1-checksums.bin index 6c772dcf5064ee8840e770d04c530d444af0973d..44d0c1af54466a971ef23b584978f9cb1ebd9bd1 100644 GIT binary patch delta 524 zcmcb(gKO$;t_>yG<1NW(xH-|ycpl@2%~PK| z6yujRT|L{^d>=%~-1yD8zdv&DYotZJU-n!VsxNSRC=26kKE{dL6@?i!vKa4e_o`*Q zD9f+(Ph?A->s_d-_U%Hm8GrLJKHR8iz&w5W6Gp!2v(_@2frw_-DgXES^-OlGmfCK! zj?s^e@$mNUEsTo1{LOLPYWDBLp|)#oUwn|!lZihoEu?qJ(>kcZpSM3g%y?RgF><5g z4%Y2JgC_=p%~PDd(VIzXdaW6g*>)iD!EE~dN+!AK-MLJD)3;XvS?BYZB)0SCG2P=~ znY49j)%K57Olop0+2_CVP7j>PWHjA(CX*S6kej}6CX)nE;y2s$y)&6IcqVOKUkkQe zo#D*%g-;oUr)$k&65k#@i|HHV^z2zod@@4s=Em%7aTGqIy7%;1zIU~J{y)PQ7#OGS z{JyDWx??4i5zEf+Tc&^=D05Ooa#sD>H8o#4tTi4U6!({2%)bPpb_etH!dxaP!YZa~ u&S8?64zyoRBj#_&_jT7Tmgk*bofNcV`t#%GZIvLV?T&`HS#T4OHUa=RF~!0F delta 269 zcmbQbo9p5Zt_>yBLsE>I8x>!$ZU^d_7&tuu zsC0U*8I##|Ao0PBMPW({|MbFKrVJpftz`S%TqXftmgzUo_iz7L#iS<3lDYFr`1HV; zOaargXEI4lpFf*PZu&wX3n+2jd-~p)Oc^}WZ(hn}00DJ|Gt(D7WfY#SHHS%jy8m3D Q()F{LB(_hS#dLrX0Ji{Vx&QzG diff --git a/.gradle/8.5/executionHistory/executionHistory.bin b/.gradle/8.5/executionHistory/executionHistory.bin index f373e08faa4d13cdf8a9402ea218f17cd9ffe9a7..070bcd1501a0b433bad536564ce49ba806a85199 100644 GIT binary patch delta 3887 zcmb7HdstM}7Vp5!Fi+TLUi;wS%mAW;%0nK4`2sPJL>{y) zzqR(-`|Pv(xlPP&GCW?!8|Dr7O1x69%p2jAdlg=#SLIcEHQq>Xloxqfuhy&c>b(Z9 zaUbqnm9X#hw#ucCtX@>(DqEbnq-Mn<%a@d|s##Y1aK_@5EAQg}!q4t+yUf2JVUDkG zG0l&sLJz2Akkc(!LFCtXhS=|#7Qx$JlPcydwI|1uB4~Q+Otg%z1{L4%H0K7a|Fe0SlKZSull>sH>qU6`7#$v``+N-u^e08a(S+ z8T6kP^7J5-3Jra7QUI?h65cw4=hArw4T#kk;i?2L5EKa}7T%QNInd-J%g8MgVsp$ujL$G46o*htYfx* zsguD?Ij&$nhMk3^O?g5-uC+2+2L6q#1lE3!t3cNx#Gy^5(coeUVf7EVQe^N*6(hjq zBZl2762Z0z%6UUWlU@y^^ySQ9(Nx4_gM~ZwGDy6H%OUTwFpKpCQz|4k69c}C9~K$> z>ny1-CkodvpNe>_)#_wubr~dZ`$yuwxmMt@D~PD`SBTJe>P46iTCZZ4XfayTt?}U3 z;xgzO(B0}%C&Jk|S{Zn*;R;b<{p&3@aQ;M+>>emfYTvL#m{UxgX9tEj6P9>zOfxE> z)QBAr3qo1!8_BxKf5s1qWaPNjabUl!k)e-#O|L!q{a-FLUd-^w8y|cOK75f>#IwVM zgLcV~DmvOxEI5uRH1JV3sp4P<5Q7cu7)hX|hm>GI?bfN*81OxB(EvhZy&8TID<;OV z;GZ8QgFjy<9mCg>?7Asn=THn($KVHP5(JwH)+kjZY^pPq3W8c|l@9?nW2Xox2LDl2 z1f)6$XKyc2$ahoVyv_lqE#$GSp45Z{&VYt|s{+tA(#!8#aS3yi?zh0>%DM1tES@I< z3LvmRtAVOM`~a<=bvyefnB0#Sh!i7_AqH;86UB|SWQt%GTX(Q^jE}YspH<*<2?tFJ zwURBzI8wXdiO%~uw0hXXaPtNsK-V=@f&@Oc$sCTrQbp3T*zi1HEaU)n+|Wz{djdHa z%@VGJMiXozH0)xd;e$kyMGsBO7tl?Eq+=>M9FTGgLY^(`^K{cYTG(9htyjz8w=rbD z(^t(6O~&XNcbRiVX>Qr0?iJ2*cixi9@`sa`I%}QbXlnbs%breykY$SBP z8$KKy{ZdslAC&UPpa7LZ1ZLPM@jc7jRqi@5?aCgFYDQr95%c1hy8fcR8<6{hl+t2K z`}>g4vIR+{yaz1*z+^B|ky{GVABlTm)YQ3Ot-hAJH8~p?DVG>>bERO@V5Vb2f|jqW za#dF@8h-S5}Kl6@HPn&U%gWrPc{SCpnq|LR%`nrf zws8kEdyj>$`o`4jYlJql!e$9&OQKTl5G9hAI!I4jak_J8>aNu*ZngcEW^*6>ZO6SO zTk?6iW}14C{!US$T}af2`tUxdyRxXNx@t%to=DsL?q}Z?_t={)x$+Bdzb@{eI|N)G z>>y!he5cZZB^}F<{?KDj{&fAergF{2QsV(MotG2REj3a1$F5?H6ygF`F05X=r|#^d`L;VdXM z7C}=Mp3PjMYWc}(-nS@Whn|}~ARq}m8?qq%%4jW?{_rdGhO(OjMg8~(Cd6*`36`@(K^$a<=z2kIINrb%j{D>lpwzI`z& zeoLL9iQB7;VBzRV zI6@9N2iX{~4(CGmO~VtJPLZkgMR_E=5zB=>UYdgLP3Iyb zAU}@F2@$W=X2baDB#%(WIV+5J!xQl&PyQ{$Pn>g>ivTo&EU^f?sL;aR5rUufFq;lf z=i^M_HcbMbqbb`zV)J0@NTTBW7r|@UJ?I%B0pgSlzLE$jv>#z}!OaBAH)TRCa delta 3581 zcmb7HeRxyl6<0a>A~6G0f`V}Lvxvco0U-1?t?ym{Vxe&?L` zoZmU`M>hX%W!z>OwPKtYFZL2uqFU6538GfiiF(l>8by<67A+zt@}gBt6q7`oXm4R% z3*t{+MAti3cf9|V*6ru`XEmtHNFRcT-nJm8YMLj1Xwc{JRtBr5=T{9KQdJooJk2|( zIyi0m!0KRi{@{T@-;n8EG(5s*U{8}h5tm(-eXR}i6EiaJ_$fx|SC;CZ<)=5dWY=p& z)y;FYSAXd4ynmIqymkBZr1P0^zr~@c?rfwt>-vG)uCw9D-`O}U9%pf2!&3(RU7$0L zvtZEyR)Dn&WT5*1Ye4QxHl<#8pW1-P7pxfji>ykZ^LU&d4RySRd&b}A+NOFpZ0ey2 zyGp*(VL-9P1N&s9pCs?dF7-3`a#Ec%kL#hN0kvVD=F5Iw> zM}ECN>WXs827j21Kz2xuT9Uo_mOB;BoAxr4FY2M}oT#;7<`H&3UL0tT#h(r5+wMfz z+SvpJ0g|imHZB>Tw6jutwU<{~1dg(@o-Aa8d(^0b|Eksi^OtNqYUcEaDsa)tq4`T< zzqPyvQ2Uk1fxt0Vgb!7mat)WxWWcRstPoG0(M17oV|+T=bS52E9%sW8K=WAZ0Ju)D ze2ibNk1Aj3PlQ$6woib!T^D6-)pFj8wlb{=ZJ#G9Yk}il!Yt!G5Dc_Xc4B$*)RR8&VzZMdi0eyX*f^4dhx4mRRKqA5ySO`};G)hXtK34`J zR#t_A<6P8zIAqMl=@P33wHJx25B#R6`^XAs4l4gmsg6F*MP;5_DY+;qBjrh#Ncq7V zGU4PU1A#)j9#ia8$}=rV(GI;EP(77WdxcHKybhUX|8I=xC_QJ=a0RdSeYojwGf%u% zS)W-mHuF_{yq4&xebd42;3@^7frFeIflqWM?7d2a#$qKP2c?Wtq3J)w$BzN88Rbev z+bX9C97ly~xJE(uY$Vd?i^wAu4Qj4a3rXx+1+D_KQ`uvRJi7PmMxypv+pN3c&}SLK zoFM_Omo!wyG*+nqBKrvN1jy;ln-r(iTx1bIhm$`4SYwNlL$dK`D01n*t*1W!$i*sf zCTNeMPfxLqgXcM80!BWk*WhJ0EB`5aX|mOX8MoM@IFc-H4>J1|M*Kj#PfF(S5?cjs zXHYU-jp^Cg^9J9FjSo@p((IS$EU{Zw9~CN<`}n)W2e2(pC|Af1Uo$l+)~3&~aJ4ve zaL%A4ojDX)$5i=HYMyQ#hD$ZtB!p)Ynx+yea5gBP-xAvJ7N3k=xn#EeaoR1CEO1Pf zfV?9HE&i+)CgZdDa!(VwN^xM&X93z)7`6CsA37Jl@o_OWeRQeOaqjw;+l)PZi2}`9 z`645(eF$giQV})^8m!JETWN6#ATx%FenjNl4&p|Vm9_{@)ayvOUw?8H>uCvfO?IW= zW`8#2C%~E=LJIbINWN^5qRu{aidvg}X65|KUffenJA3)xxx$~Rt(&tYc;g9l{izov zk)39T{{^iM-7QJ(yVtKAoB>}Hnk?AO2^#ME_I-<+ZGSmYexvk_?`QQ|hq_dU4=eYW zE$H&m29e$glHrCvx@44?DT8t^dl-k3<#SL%!{>>)PF_$U!$d-e;7_A6W0(PegJU8$8tjb@^FDjnX`1aMA zx&5}|vO7KvZ7rz=wD0AkHI&pN?sz@g7SfTsdpH}89gD2dc28F)-3NV|FjDahkt{Y0 z=ghb`g53u;Pwv2~8teVQNFw>$=VaLfBe~a+HAX&emhR$5rge#W;}Gbrn(r9T-F8y#2zDyZuf;{?kGN3bUvu zK3pU}Q6&riw>m32vxvIEC*?QC({)xKmKV``EcRkLoGsKNXN+9e^k?j&u|J1mj>(pu z)4iz3*L#-U?QInaY8?E8FNlx(02>z?GSlWg9h@?|a>lQx+}Z!!S#wRxVlgo5LUZxa z_y6~D5#C>9s)T=coC^L=`BApU_RL^cRrmQ*H=i{&aczZ7w>P((M9CH#wV7ky=Vgb? z3m=>Bx;DY+Tlmo($h*mgx(O^@ds_NBLH|!axHSz9CHuQp^L? z$EB&~Gv22@FHJA6*Za_RI*C(nl-z_r<43BSr0ILxYz69frRn8~+=%7sJb^|sjT9OV a8mTnyp^-+zNyA0MO(VTkotM#j-Twjd%Sh?~ diff --git a/.gradle/8.5/executionHistory/executionHistory.lock b/.gradle/8.5/executionHistory/executionHistory.lock index a7c86f1b824eee6a07b7d5ba60b1b105f282879b..fdf7eea3e26a5178661e5dda6913f28ff799d9eb 100644 GIT binary patch literal 17 VcmZSnD(U&&^z=?{1~3q{1^_f;1YQ6D literal 17 VcmZSnD(U&&^z=?{1~3q{001;#1X}qd@2GP$piw@Ja87ofQ6i`1zVni zal)&n9N~0GWqr}aUjZKCE7)$P%INlzqc@Yci9KQ>(e+SUC@^P~Nf91+LJ5gm0cdeB*l{hq%u|1oq|?ybo1VDyqx?aH(c=ZUg$GuX z(jYj=qst3G>9+Twr?e5z@j)V~SPrjHAm1o!=dsNPBH%+27$VvvI6~0}OF50vx=kKj z9EnSYseHPnAz=I~<8cq^ccRUJ5q$bcb4g!bn~7C5Dsng*j*6+sRHN(G>{F*72x-jn`=;O%4hJ0TnZMu7x_k_H zZ###GTPDMD%+!`*JIfOwPg~B;Ahs*Y;0puG{wMQ(O1%`OeLFyfTTkg=Jx* z0?82*T!N<*Cxws7wK=mVppa}%X>wJVpQnAmR>UeWh@<4BXaTN9xJAX{UdOw{AQ+E%9^b$- zDu%uSAtx70(ix9=a&QVqr88nKA0;ovIENK&xHAU@lQB6eRK>(G6|N&Eb6tKzud3IN zcv8!F4K)(_wTS8bcT@9VEu3- zS3S5}_M+PVbI~b&SEw6c@A;PnP?wqgrOLs#cir4j!A*!f>g+xyEKMCvF4BoUphG^ljJGwMzL?@T!eSlISQ9UHF;*&!Ral%)w0Gax zcYUc~w#bz*KNq=vF0*`uSXM?!R)mYIPPcfi5>)8d9mEd|Zj-&&i!~t!v&Z}ol9HD< delta 629 zcmY+i+94-tik zC}k&!>Op1Z5;e1n5zO`1VWcyJOwA~0{qaFZn9I`K=1b@8z~`LrIWD)_@mk$cJ4Wtz z)r}>t>iXpMs-=xGaza951ndzSiF`gr+HuB0oMNDuL}|oA1&LFL^sD4#C^?T0+Gvh& zTqkLBWJt(ssc#)d_Run6>!G_Sadzsn7&>ODO7LE~J&|;aLq0kbE=(%W5nE*#D6myd zHi(x|Fe1m_N)jhLI(ScsqEo*tBsf_X3tlyG2+LM*Q<0qvTa=>71Y9Y`%;3_{SYM|; zN*=IyXFc?ZjAE#&ET|}tV~AgZ%yJwS)deI`0EF>=yKvxLqB*T zYt7MDzE5w9f4-#3mlRri=}O_Ags;Hk2MkzfpRitTYko$h`BFb^oEs%Unk|WuDR|0w z(!GS!uH_x}bIyEULTSvXpEY05)CU>Y7!;+X0 z)92v}e-$&7qWam#rJZaxw`!{2KiQP~ZA~!6n2tO6;-du%u$y6fG!191HP=G0L_@Ur zVi*Fg632%@E5aC4$uUnCGzDnYQLV^SGV^L~`}@bfFW){sIE_yZ^5gIPA+0qxX&{6g LNfS;3t4R3=z=Y_z diff --git a/.gradle/8.5/fileHashes/fileHashes.lock b/.gradle/8.5/fileHashes/fileHashes.lock index eabc46119726a0d8461980c2a84d01ed931cb114..aa5d31e1678a46cbb2bc48bc0bfbfbe3326fcce0 100644 GIT binary patch literal 17 VcmZR+zH2oTgY?$~1~Ay1001%X1jPUV literal 17 VcmZR+zH2oTgY?$~1~Axc1OPF?1eX8+ diff --git a/.gradle/8.5/fileHashes/resourceHashesCache.bin b/.gradle/8.5/fileHashes/resourceHashesCache.bin index 7651bffa0b8cc816c2bc5f8a798b97ba1415d4b8..b56e6467cecfa74290dbb88a15ee0965c1f77e44 100644 GIT binary patch delta 1017 zcmX?pis$(`o((1vjNzM2B_{Fkg&jG=ZZVAk1enz-H}6%MEX-&*J=>GXcJnfeTg{9n zn;ZA+Wa8%u+IEA%c^*WaI^$-;{R_GHZRSk=H#yZ1BB*+P^U0GdS^4k%{qV|6{03BT z?dHt$yEz#@ZqB{AAdE3#JD&k#BP0LitQ=PUZT}!@)P%RU88TMMGbsvgmkMOe5oC0k z-k!vm$e(vBbM>RCU!f*_+$ zy}gKWAus>JgELOKT<3>~s=H2?Z(uBDES&zOo2hksMI+if$6#LnE19kSTN0F zV~pK?-;U`NFQ4=eF^w8ysGZ8&=ld{CWa59WxzO}n;uWY#dnfO;vDhB(&m=6scygnn z2S3=H>D*GxtF|xBXYvtYwBG)qnQ0{(zw1{gTSEpns6Nx}6S|mgi!sjJs8}Hcv72iP zQy3Sc&h*56Op^SO98do&Ju?fcdiM6heN38?{JyXLH&(4%0u{Z#ed2Yd(@czp8xnmf7^x)l7Uw+mA(wgv*8hYwOjjzp(C^2~fQp$QzPCEC|8^hYu`R+WMsP*~I52 z4IC3=tELy$F$o)<4}aOwmpYeEs96_4^VCI+_I{<{^Aslv3P)W|_P zNKQa$bGM)4+K-qj?k%1C&~)|R6BFJ^Evo8U@(7_K1{PXO(!LuFck-^g%v^q5`saa( z2qiET1rBj@MFPW@6D<`@;v6O*S005G|K?nc< delta 307 zcmV-30nGmA&J4%O46rm90amj$7?KRL-z13>0U?)DNdi@~ra0Pz0Wq_Iytx9iQoooA zvjWPX1+%)(vIzyL009L-m-)j2X_tD$0!Opi*)&!GU$+i00f7Oxr!fI>9|j=#00H}d zw;mJ%i2=8dQUUi50YI0BVgX^cV*$Vnx7cq1=mNLcasikPmxzD?Y5@_KtcwEWm#K^b zFt@0I0eyx6Q@17v0@(()7Y_o!4421N0Z6wu90KtR0w3A2Avh41ZSDdNw?H@oo(2J1 zx8FVj$_=-{Oag}jlg2zRw~tQ(6A%HJu_1U5mu>C>4wnlW1FE;9Zvspd0Xnzvg951r zx3P)>(iQ=Mu_2HWmu>C>4z~)l0#*tEDwkot0vWfGz5*Z_x60Q7!U6#Su^|u?w{7kM F+BRYobD01D diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock index a34f41fba5cf07687c9ebc93f91eeb92813c4da8..4e38d46de2be73ed0efffed504a5e495b64be816 100644 GIT binary patch literal 17 VcmZSnKTT<6%cI}H3}7Js4gf(a1|0wZ literal 17 VcmZSnKTT<6%cI}H3}7Js0suiI1{VMT diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe index 0767e4bceadc92b1f36fa9f11bede6c5a67bfdda..31823eb31023979a10bcba985b085cfac667e840 100644 GIT binary patch literal 8 PcmZQzV4S-1``Ty#3Uvcd literal 8 PcmZQzV4S+GE2I?w2$}-0 diff --git a/src/main/java/me/trouper/sentinel/data/config/NBTConfig.java b/src/main/java/me/trouper/sentinel/data/config/NBTConfig.java index 476e0b5..704e1b6 100644 --- a/src/main/java/me/trouper/sentinel/data/config/NBTConfig.java +++ b/src/main/java/me/trouper/sentinel/data/config/NBTConfig.java @@ -17,6 +17,7 @@ public class NBTConfig implements JsonSerializable { public RateLimit rateLimit = new RateLimit(); public class RateLimit { + public int maxOverhead = 32768; public int rateLimitBytes = 16348; public int byteDecay = 1024; // Every Minute public int rateLimitItems = 10; diff --git a/src/main/java/me/trouper/sentinel/data/storage/NBTStorage.java b/src/main/java/me/trouper/sentinel/data/storage/NBTStorage.java index 415fb8b..2a0e9a4 100644 --- a/src/main/java/me/trouper/sentinel/data/storage/NBTStorage.java +++ b/src/main/java/me/trouper/sentinel/data/storage/NBTStorage.java @@ -1,39 +1,79 @@ package me.trouper.sentinel.data.storage; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable; import me.trouper.sentinel.Sentinel; import org.bukkit.inventory.ItemStack; import java.io.*; -import java.util.*; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; public class NBTStorage implements JsonSerializable { - @Override - public File getFile() { - File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/storage/nbt.json"); - file.getParentFile().mkdirs(); - return file; - } - + + // Mapping from file name to owner UUID (as a String) public Map caughtItems = new HashMap<>(); - public static ItemStack toItem(String serializedString) { - if (serializedString.equals("null")) return null; - byte[] decodedBytes = Base64.getDecoder().decode(serializedString); - String mapString = new String(decodedBytes); - // Remove the curly braces and split by commas to get key-value pairs - String[] keyValuePairs = mapString.substring(1, mapString.length() - 1).split(", "); - Map deserializedMap = new HashMap<>(); - for (String pair : keyValuePairs) { - String[] keyValue = pair.split("="); - deserializedMap.put(keyValue[0], keyValue[1]); + private final File mappingFile; + private final File storageDir; + + public NBTStorage() { + // Create the storage directory: /storage/nbt/ inside the plugin data folder + File dataFolder = Sentinel.getInstance().getDirector().io.getDataFolder(); + storageDir = new File(dataFolder, "storage/nbt"); + if (!storageDir.exists()) { + storageDir.mkdirs(); } - ItemStack item = ItemStack.deserialize(deserializedMap); - return item; + // The mapping file that stores the file-name to owner UUID mapping + mappingFile = new File(dataFolder, "storage/nbt.json"); + mappingFile.getParentFile().mkdirs(); + } + + /** + * Stores an ItemStack's serialized NBT to a unique file + * and maps the generated file name to the owner UUID. + * + * @param item the ItemStack to store + * @param owner the owner's UUID + */ + public void storeItem(ItemStack item, UUID owner) { + // Generate a unique file name with a .nbt extension + String fileName = UUID.randomUUID().toString() + ".nbt"; + File file = new File(storageDir, fileName); + try (FileOutputStream fos = new FileOutputStream(file); + OutputStreamWriter writer = new OutputStreamWriter(fos)) { + + String nbt = serializeItem(item); + writer.write(nbt); + } catch (IOException e) { + e.printStackTrace(); + } + // Add mapping: file name -> owner UUID (as string) + caughtItems.put(fileName, owner.toString()); + save(); + } + + /** + * Placeholder for item serialization. + * Replace this with an actual NBT serialization logic. + * + * @param item the ItemStack to serialize + * @return a String representing the NBT data of the item + */ + private String serializeItem(ItemStack item) { + + return item.toString(); } - public static String toB64(ItemStack itemStack) { - Map serializedMap = itemStack.serialize(); - return Base64.getEncoder().encodeToString(serializedMap.toString().getBytes()); + // Make a deserialize method too. + + + @Override + public File getFile() { + return mappingFile; } + } diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/players/CreativeHotbar.java b/src/main/java/me/trouper/sentinel/server/events/violations/players/CreativeHotbar.java index fd30ad9..303285d 100644 --- a/src/main/java/me/trouper/sentinel/server/events/violations/players/CreativeHotbar.java +++ b/src/main/java/me/trouper/sentinel/server/events/violations/players/CreativeHotbar.java @@ -6,8 +6,8 @@ import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.data.storage.NBTStorage; import me.trouper.sentinel.server.events.violations.AbstractViolation; import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; -import me.trouper.sentinel.server.functions.itemchecks.ItemCheck; -import me.trouper.sentinel.server.functions.itemchecks.RateLimitCheck; +import me.trouper.sentinel.server.functions.hotbar.items.ItemCheck; +import me.trouper.sentinel.server.functions.hotbar.items.RateLimitCheck; import me.trouper.sentinel.server.gui.Items; import me.trouper.sentinel.server.gui.MainGUI; import me.trouper.sentinel.server.gui.config.AntiNukeGUI; @@ -45,7 +45,7 @@ public class CreativeHotbar extends AbstractViolation { if (!new RateLimitCheck().passes(new Pair<>(p,i))) { List punishmentCommands = new ArrayList<>(); for (String punishmentCommand : Sentinel.getInstance().getDirector().io.nbtConfig.rateLimit.punishmentCommands) { - punishmentCommands.add(punishmentCommand.formatted()); + punishmentCommands.add(punishmentCommand.formatted(RateLimitCheck.dataUsed.get(p.getUniqueId()),Sentinel.getInstance().getDirector().io.nbtConfig.rateLimit.rateLimitBytes)); } ServerUtils.verbose("Player flags rate limit, performing action"); @@ -55,7 +55,7 @@ public class CreativeHotbar extends AbstractViolation { .cancel(true) .punish(true) .deop(Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.deop) - .setPunishmentCommands(); + .setPunishmentCommands(punishmentCommands); runActions( Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.grab, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.nbtItem), diff --git a/src/main/java/me/trouper/sentinel/server/functions/hotbar/AbstractCheck.java b/src/main/java/me/trouper/sentinel/server/functions/hotbar/AbstractCheck.java new file mode 100644 index 0000000..4796ad2 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/functions/hotbar/AbstractCheck.java @@ -0,0 +1,9 @@ +package me.trouper.sentinel.server.functions.hotbar; + +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.data.config.NBTConfig; + +public abstract class AbstractCheck { + public NBTConfig config = Sentinel.getInstance().getDirector().io.nbtConfig; + public abstract boolean passes(T input); +} diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EntityCheck.java b/src/main/java/me/trouper/sentinel/server/functions/hotbar/entities/EntityCheck.java similarity index 87% rename from src/main/java/me/trouper/sentinel/server/functions/itemchecks/EntityCheck.java rename to src/main/java/me/trouper/sentinel/server/functions/hotbar/entities/EntityCheck.java index 37ade6a..092dda5 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EntityCheck.java +++ b/src/main/java/me/trouper/sentinel/server/functions/hotbar/entities/EntityCheck.java @@ -1,7 +1,9 @@ -package me.trouper.sentinel.server.functions.itemchecks; +package me.trouper.sentinel.server.functions.hotbar.entities; import de.tr7zw.changeme.nbtapi.NBT; -import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.functions.hotbar.AbstractCheck; +import me.trouper.sentinel.server.functions.hotbar.misc.InventoryCheck; +import me.trouper.sentinel.server.functions.hotbar.items.ItemCheck; import me.trouper.sentinel.utils.InventoryUtils; import me.trouper.sentinel.utils.ServerUtils; import org.bukkit.entity.Entity; @@ -43,7 +45,7 @@ public class EntityCheck extends AbstractCheck { } } if (!entity.getPassengers().isEmpty()) { - if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowRecursion) { + if (!config.allowRecursion) { ServerUtils.verbose("Entity recursion not allowed."); return false; } @@ -60,7 +62,7 @@ public class EntityCheck extends AbstractCheck { ServerUtils.verbose("Entity death time check failed."); failsTiming.set(true); } - if (nbt.hasTag("Hurttime") && nbt.getInteger("Hurttime") < 1) { + if (nbt.hasTag("HurtTime") && nbt.getInteger("HurtTime") < 1) { ServerUtils.verbose("Entity hurt time check failed."); failsTiming.set(true); } diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EntitySnapshotCheck.java b/src/main/java/me/trouper/sentinel/server/functions/hotbar/entities/EntitySnapshotCheck.java similarity index 83% rename from src/main/java/me/trouper/sentinel/server/functions/itemchecks/EntitySnapshotCheck.java rename to src/main/java/me/trouper/sentinel/server/functions/hotbar/entities/EntitySnapshotCheck.java index b9e1ce6..f647e52 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EntitySnapshotCheck.java +++ b/src/main/java/me/trouper/sentinel/server/functions/hotbar/entities/EntitySnapshotCheck.java @@ -1,5 +1,6 @@ -package me.trouper.sentinel.server.functions.itemchecks; +package me.trouper.sentinel.server.functions.hotbar.entities; +import me.trouper.sentinel.server.functions.hotbar.AbstractCheck; import me.trouper.sentinel.utils.ServerUtils; import org.bukkit.Bukkit; import org.bukkit.Location; diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EquipmentCheck.java b/src/main/java/me/trouper/sentinel/server/functions/hotbar/entities/EquipmentCheck.java similarity index 57% rename from src/main/java/me/trouper/sentinel/server/functions/itemchecks/EquipmentCheck.java rename to src/main/java/me/trouper/sentinel/server/functions/hotbar/entities/EquipmentCheck.java index af83534..023046f 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EquipmentCheck.java +++ b/src/main/java/me/trouper/sentinel/server/functions/hotbar/entities/EquipmentCheck.java @@ -1,5 +1,7 @@ -package me.trouper.sentinel.server.functions.itemchecks; +package me.trouper.sentinel.server.functions.hotbar.entities; +import me.trouper.sentinel.server.functions.hotbar.AbstractCheck; +import me.trouper.sentinel.server.functions.hotbar.items.ItemCheck; import me.trouper.sentinel.utils.ServerUtils; import org.bukkit.entity.Mob; import org.bukkit.inventory.ItemStack; @@ -11,8 +13,10 @@ public class EquipmentCheck extends AbstractCheck { public boolean passes(Mob mob) { ServerUtils.verbose("Running mob check."); for (EquipmentSlot slot : EquipmentSlot.values()) { + if (mob.getEquipment().getItem(slot).isEmpty()) continue; ItemStack item = mob.getEquipment().getItem(slot); - if (item != null && !new ItemCheck().passes(item)) { + if (!new ItemCheck().passes(item)) { + ServerUtils.verbose("Equipment slot did not pass."); return false; } } diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EnchantmentCheck.java b/src/main/java/me/trouper/sentinel/server/functions/hotbar/items/EnchantmentCheck.java similarity index 96% rename from src/main/java/me/trouper/sentinel/server/functions/itemchecks/EnchantmentCheck.java rename to src/main/java/me/trouper/sentinel/server/functions/hotbar/items/EnchantmentCheck.java index 6ad5d4d..bbaaacb 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EnchantmentCheck.java +++ b/src/main/java/me/trouper/sentinel/server/functions/hotbar/items/EnchantmentCheck.java @@ -1,6 +1,7 @@ -package me.trouper.sentinel.server.functions.itemchecks; +package me.trouper.sentinel.server.functions.hotbar.items; import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.functions.hotbar.AbstractCheck; import me.trouper.sentinel.utils.ServerUtils; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemStack; @@ -10,7 +11,12 @@ import java.util.Map; import static org.bukkit.enchantments.Enchantment.MENDING; -public class EnchantmentCheck { +public class EnchantmentCheck extends AbstractCheck { + + @Override + public boolean passes(ItemStack input) { + return !hasIllegalEnchants(input); + } public boolean hasIllegalEnchants(ItemStack item) { ServerUtils.verbose("Checking item for illegal enchants: ", item.getType().name()); @@ -119,4 +125,6 @@ public class EnchantmentCheck { return level > maxLevel; } + + } diff --git a/src/main/java/me/trouper/sentinel/server/functions/hotbar/items/ItemCheck.java b/src/main/java/me/trouper/sentinel/server/functions/hotbar/items/ItemCheck.java new file mode 100644 index 0000000..9e0ce9a --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/functions/hotbar/items/ItemCheck.java @@ -0,0 +1,81 @@ +package me.trouper.sentinel.server.functions.hotbar.items; + +import de.tr7zw.changeme.nbtapi.NBT; +import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.functions.hotbar.AbstractCheck; +import me.trouper.sentinel.server.functions.hotbar.misc.BlockStateCheck; +import me.trouper.sentinel.server.functions.hotbar.misc.InventoryCheck; +import me.trouper.sentinel.server.functions.hotbar.nbt.ComponentCheck; +import me.trouper.sentinel.utils.InventoryUtils; +import me.trouper.sentinel.utils.ServerUtils; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.Arrays; + +public class ItemCheck extends AbstractCheck { + + + @Override + public boolean passes(ItemStack item) { + try { + return scan(item); + } catch (Exception ex) { + Sentinel.getInstance().getLogger().warning("Caught an exception while handling an item check: " + Arrays.toString(ex.getStackTrace())); + return false; + } + } + + + private boolean scan(ItemStack item) { + ServerUtils.verbose("Checking item: " + item.getType().name()); + + // No metadata? Nothing to check. + if (item.getItemMeta() == null) { + ServerUtils.verbose("Item passes because it has no metadata."); + return true; + } + + if (!new MetaCheck().passes(item)) { + ServerUtils.verbose("Item failed metadata check."); + return false; + } + + + // NBT-based checks + ReadWriteNBT nbt = NBT.itemStackToNBT(item); + ReadWriteNBT components = nbt.getCompound("components"); + if (components != null) { + if (!new ComponentCheck().passes(components)) { + ServerUtils.verbose("Components check failed."); + return false; + } + } + + // Spawn egg checks. + if (!new SpawnEggCheck().passes(item)) { + ServerUtils.verbose("Spawn egg check failed."); + return false; + } + + if (!new BlockStateCheck().passes(item)) { + ServerUtils.verbose("Block State check failed."); + return false; + } + + // Check for an inventory inside the item. + Inventory inv = InventoryUtils.getInventory(item); + if (inv != null) { + ServerUtils.verbose("Item contains an inventory: " + inv); + if (!new InventoryCheck().passes(inv)) { + ServerUtils.verbose("Item failed inventory check."); + return false; + } + } + + ServerUtils.verbose("Item passed all checks."); + return true; + } +} diff --git a/src/main/java/me/trouper/sentinel/server/functions/hotbar/items/MetaCheck.java b/src/main/java/me/trouper/sentinel/server/functions/hotbar/items/MetaCheck.java new file mode 100644 index 0000000..0caf5e2 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/functions/hotbar/items/MetaCheck.java @@ -0,0 +1,65 @@ +package me.trouper.sentinel.server.functions.hotbar.items; + +import me.trouper.sentinel.server.functions.hotbar.AbstractCheck; +import me.trouper.sentinel.utils.ServerUtils; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.inventory.meta.BundleMeta; +import org.bukkit.inventory.meta.ItemMeta; + +public class MetaCheck extends AbstractCheck { + + @Override + public boolean passes(ItemStack item) { + ItemMeta meta = item.getItemMeta(); + // Name, lore, potion, attribute and enchantment checks. + if (!config.allowName && meta.hasDisplayName()) { + ServerUtils.verbose("Custom names not allowed."); + return false; + } + if (!config.allowLore && meta.hasLore()) { + ServerUtils.verbose("Custom lore not allowed."); + return false; + } + if (!config.allowBooks && meta instanceof BookMeta) { + ServerUtils.verbose("Item failed book check."); + return false; + } + if (!config.allowPotions && + (item.getType().equals(Material.POTION) || + item.getType().equals(Material.SPLASH_POTION) || + item.getType().equals(Material.LINGERING_POTION))) { + ServerUtils.verbose("Potions not allowed."); + return false; + } + if (!config.allowAttributes && meta.hasAttributeModifiers()) { + ServerUtils.verbose("Attribute modifiers not allowed."); + return false; + } + if (config.globalMaxEnchant != 0 && new EnchantmentCheck().hasIllegalEnchants(item)) { + ServerUtils.verbose("Illegal enchantments found."); + return false; + } + // Recursion check for use-remainder items. + if (meta.hasUseRemainder()) { + if (!config.allowRecursion) { + ServerUtils.verbose("Recursion not allowed."); + return false; + } + if (meta.getUseRemainder() != null && !passes(meta.getUseRemainder())) { + ServerUtils.verbose("Use remainder item failed check."); + return false; + } + } + + // Bundle check – recursively check the contained items. + if (item.getType().name().contains("_BUNDLE") && meta instanceof BundleMeta bm) { + for (ItemStack bundleItem : bm.getItems()) { + if (!passes(bundleItem)) return false; + } + } + + return true; + } +} diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/RateLimitCheck.java b/src/main/java/me/trouper/sentinel/server/functions/hotbar/items/RateLimitCheck.java similarity index 73% rename from src/main/java/me/trouper/sentinel/server/functions/itemchecks/RateLimitCheck.java rename to src/main/java/me/trouper/sentinel/server/functions/hotbar/items/RateLimitCheck.java index afda1cf..f77981a 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/RateLimitCheck.java +++ b/src/main/java/me/trouper/sentinel/server/functions/hotbar/items/RateLimitCheck.java @@ -1,16 +1,13 @@ -package me.trouper.sentinel.server.functions.itemchecks; +package me.trouper.sentinel.server.functions.hotbar.items; import de.tr7zw.changeme.nbtapi.NBTItem; import io.github.itzispyder.pdk.utils.misc.Pair; import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.functions.hotbar.AbstractCheck; import me.trouper.sentinel.utils.ServerUtils; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import org.bukkit.scheduler.BukkitTask; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -36,20 +33,19 @@ public class RateLimitCheck extends AbstractCheck> { ServerUtils.verbose("Current Player used items: " + currentUsed); currentUsed++; itemsUsed.put(uuid,currentUsed); - return currentUsed <= Sentinel.getInstance().getDirector().io.nbtConfig.rateLimit.rateLimitItems; + return currentUsed <= config.rateLimit.rateLimitItems; } private boolean dataLimit(Player player, UUID uuid, ItemStack item) { - int itemData = 0; int currentData = dataUsed.getOrDefault(uuid,0); ServerUtils.verbose("Current Player used data: " + currentData); try { NBTItem nbt = new NBTItem(item); - itemData = nbt.toString().length(); + int itemData = nbt.toString().length(); ServerUtils.verbose("Item data: " + itemData); - currentData += itemData; + if (currentData < config.rateLimit.maxOverhead) currentData += itemData; } catch (Exception e) { Sentinel.getInstance().getLogger().warning("Could not determine size of item. Blocking."); Sentinel.getInstance().getLogger().warning(Arrays.toString(e.getStackTrace())); @@ -60,24 +56,24 @@ public class RateLimitCheck extends AbstractCheck> { ServerUtils.verbose("New Player used data: " + currentData); - return currentData <= Sentinel.getInstance().getDirector().io.nbtConfig.rateLimit.rateLimitBytes; + return currentData <= config.rateLimit.rateLimitBytes; } - public static void decayData() { + public void decayData() { for (UUID uuid : dataUsed.keySet()) { int currentData = dataUsed.get(uuid); if (currentData > 0) { - currentData -= Sentinel.getInstance().getDirector().io.nbtConfig.rateLimit.byteDecay; + currentData -= config.rateLimit.byteDecay; dataUsed.put(uuid, Math.max(0, currentData)); } } } - public static void decayItems() { + public void decayItems() { for (UUID uuid : itemsUsed.keySet()) { int currentItems = itemsUsed.get(uuid); if (currentItems > 0) { - currentItems -= Sentinel.getInstance().getDirector().io.nbtConfig.rateLimit.itemDecay; + currentItems -= config.rateLimit.itemDecay; itemsUsed.put(uuid, Math.max(0, currentItems)); } } diff --git a/src/main/java/me/trouper/sentinel/server/functions/hotbar/items/SpawnEggCheck.java b/src/main/java/me/trouper/sentinel/server/functions/hotbar/items/SpawnEggCheck.java new file mode 100644 index 0000000..ac5e7e3 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/functions/hotbar/items/SpawnEggCheck.java @@ -0,0 +1,50 @@ +package me.trouper.sentinel.server.functions.hotbar.items; + +import de.tr7zw.changeme.nbtapi.NBT; +import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT; +import me.trouper.sentinel.server.functions.hotbar.AbstractCheck; +import me.trouper.sentinel.server.functions.hotbar.entities.EntitySnapshotCheck; +import me.trouper.sentinel.server.functions.hotbar.nbt.EntityDataCheck; +import me.trouper.sentinel.utils.ServerUtils; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SpawnEggMeta; + +public class SpawnEggCheck extends AbstractCheck { + + @Override + public boolean passes(ItemStack item) { + ServerUtils.verbose("Running spawn egg checks on item: ",item.getType().name()); + if (!item.getType().name().toLowerCase().contains("spawn_egg")) return true; + if (!SpawnEggCheck.entityMatches(item)) { + ServerUtils.verbose("Spawn egg entity doesn't match item type."); + return false; + } + ReadWriteNBT nbt = NBT.itemStackToNBT(item); + ReadWriteNBT components = nbt.getCompound("components"); + if (components != null) { + var entityData = components.getCompound("minecraft:entity_data"); + if (!new EntityDataCheck().passes(entityData)) { + ServerUtils.verbose("Spawn egg entity data check failed."); + return false; + } + } + + if (item.hasItemMeta() && item.getItemMeta() instanceof SpawnEggMeta sem) { + if (sem.getSpawnedEntity() != null && !new EntitySnapshotCheck().passes(sem.getSpawnedEntity())) { + ServerUtils.verbose("Spawn egg entity snapshot check failed."); + return false; + } + } + + return true; + } + + public static boolean entityMatches(ItemStack item) { + if (item.hasItemMeta() && item.getItemMeta() instanceof SpawnEggMeta sem) { + String eggEntityName = item.getType().name().replace("_SPAWN_EGG", ""); + return sem.getSpawnedEntity() != null && + sem.getSpawnedEntity().getEntityType().name().equals(eggEntityName); + } + return false; + } +} diff --git a/src/main/java/me/trouper/sentinel/server/functions/hotbar/misc/BlockStateCheck.java b/src/main/java/me/trouper/sentinel/server/functions/hotbar/misc/BlockStateCheck.java new file mode 100644 index 0000000..21009d5 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/functions/hotbar/misc/BlockStateCheck.java @@ -0,0 +1,100 @@ +package me.trouper.sentinel.server.functions.hotbar.misc; + +import me.trouper.sentinel.server.functions.hotbar.AbstractCheck; +import me.trouper.sentinel.server.functions.hotbar.entities.EntitySnapshotCheck; +import me.trouper.sentinel.server.functions.hotbar.items.ItemCheck; +import me.trouper.sentinel.utils.ServerUtils; +import org.bukkit.Material; +import org.bukkit.block.*; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BlockStateMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.spawner.TrialSpawnerConfiguration; + +public class BlockStateCheck extends AbstractCheck { + @Override + public boolean passes(ItemStack item) { + ItemMeta meta = item.getItemMeta(); + + if (!(meta instanceof BlockStateMeta blockStateMeta)) { + ServerUtils.verbose("Item passes due to not being a block state meta"); + return true; + } + + if (item.getType().name().contains("CAMPFIRE") ) { + BlockState bs = blockStateMeta.getBlockState(); + if (bs instanceof Campfire campfire) { + for (int slot = 0; slot < 4; slot++) { + org.bukkit.inventory.ItemStack campfireItem = campfire.getItem(slot); + if (campfireItem != null && !new ItemCheck().passes(campfireItem)) { + ServerUtils.verbose("Campfire item failed check."); + return false; + } + } + } + } + + // Lectern and Chiseled Bookshelf check (by validating their inventories). + if (item.getType().equals(Material.LECTERN)) { + BlockState bs = blockStateMeta.getBlockState(); + if (bs instanceof Lectern lectern) { + if (!new InventoryCheck().passes(lectern.getInventory())) { + ServerUtils.verbose("Lectern inventory failed check."); + return false; + } + } + } + if (item.getType().equals(Material.CHISELED_BOOKSHELF)) { + BlockState bs = blockStateMeta.getBlockState(); + if (bs instanceof ChiseledBookshelf bookshelf) { + if (!new InventoryCheck().passes(bookshelf.getInventory())) { + ServerUtils.verbose("Chiseled bookshelf inventory failed check."); + return false; + } + } + } + + // Spawner check. + if (item.getType().equals(Material.SPAWNER)) { + BlockState bs = blockStateMeta.getBlockState(); + if (bs instanceof CreatureSpawner spawner) { + if (spawner.getSpawnedEntity() != null) { + if (spawner.getSpawnedEntity().getEntityType().equals(EntityType.FALLING_BLOCK) || + spawner.getSpawnedEntity().getEntityType().equals(EntityType.COMMAND_BLOCK_MINECART)) { + ServerUtils.verbose("Spawner contains disallowed entity type."); + return false; + } + if (!new EntitySnapshotCheck().passes(spawner.getSpawnedEntity())) { + ServerUtils.verbose("Spawner entity snapshot check failed."); + return false; + } + } + } + } + + // Trial Spawner check. + if (item.getType() == Material.TRIAL_SPAWNER) { + BlockState bs = blockStateMeta.getBlockState(); + if (bs instanceof TrialSpawner spawner) { + ServerUtils.verbose("Running trial spawner check."); + if (spawner.getNormalConfiguration() != null) { + TrialSpawnerConfiguration config = spawner.getNormalConfiguration(); + if (config.getSpawnedEntity() != null && !new EntitySnapshotCheck().passes(config.getSpawnedEntity())) { + ServerUtils.verbose("Trial Spawner failed check: Normal entity snapshot not allowed."); + return false; + } + } + if (spawner.getOminousConfiguration() != null) { + TrialSpawnerConfiguration config = spawner.getOminousConfiguration(); + if (config.getSpawnedEntity() != null && !new EntitySnapshotCheck().passes(config.getSpawnedEntity())) { + ServerUtils.verbose("Trial Spawner failed check: Ominous entity snapshot not allowed."); + return false; + } + } + } + } + + return true; + } +} diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/InventoryCheck.java b/src/main/java/me/trouper/sentinel/server/functions/hotbar/misc/InventoryCheck.java similarity index 56% rename from src/main/java/me/trouper/sentinel/server/functions/itemchecks/InventoryCheck.java rename to src/main/java/me/trouper/sentinel/server/functions/hotbar/misc/InventoryCheck.java index 0d442f2..8f4c6c3 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/InventoryCheck.java +++ b/src/main/java/me/trouper/sentinel/server/functions/hotbar/misc/InventoryCheck.java @@ -1,8 +1,7 @@ -package me.trouper.sentinel.server.functions.itemchecks; +package me.trouper.sentinel.server.functions.hotbar.misc; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.server.functions.itemchecks.AbstractCheck; -import me.trouper.sentinel.server.functions.itemchecks.ItemCheck; +import me.trouper.sentinel.server.functions.hotbar.AbstractCheck; +import me.trouper.sentinel.server.functions.hotbar.items.ItemCheck; import me.trouper.sentinel.utils.InventoryUtils; import me.trouper.sentinel.utils.ServerUtils; import org.bukkit.inventory.Inventory; @@ -11,16 +10,20 @@ import org.bukkit.inventory.ItemStack; public class InventoryCheck extends AbstractCheck { @Override - public boolean passes(Inventory inventory) { + public boolean passes(Inventory inv) { ServerUtils.verbose("Running Inventory Check"); - for (ItemStack item : inventory.getContents()) { - if (item == null || item.getType().isAir()) continue; - if (!new ItemCheck().passes(item)) { + + for (ItemStack i : inv.getContents()) { + if (i == null || i.getType().isAir()) continue; + if (!new ItemCheck().passes(i)) { ServerUtils.verbose("Inventory item failed check."); return false; } - Inventory subInventory = InventoryUtils.getInventory(item); - if (subInventory != null && !Sentinel.getInstance().getDirector().io.nbtConfig.allowRecursion) return false; + Inventory subInventory = InventoryUtils.getInventory(i); + if (subInventory != null && !config.allowRecursion) { + ServerUtils.verbose("Recursion is disabled. Failing check."); + return false; + } if (subInventory != null && !passes(subInventory)) { ServerUtils.verbose("Sub-inventory failed check."); return false; @@ -28,5 +31,5 @@ public class InventoryCheck extends AbstractCheck { } ServerUtils.verbose("Inventory passed all checks."); return true; - } + } } diff --git a/src/main/java/me/trouper/sentinel/server/functions/hotbar/nbt/ComponentCheck.java b/src/main/java/me/trouper/sentinel/server/functions/hotbar/nbt/ComponentCheck.java new file mode 100644 index 0000000..cb682b7 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/functions/hotbar/nbt/ComponentCheck.java @@ -0,0 +1,32 @@ +package me.trouper.sentinel.server.functions.hotbar.nbt; + +import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT; +import me.trouper.sentinel.server.functions.hotbar.AbstractCheck; +import me.trouper.sentinel.utils.ServerUtils; + +public class ComponentCheck extends AbstractCheck { + + @Override + public boolean passes(ReadWriteNBT components) { + ServerUtils.verbose("Checking Consumable & tool"); + if (!config.allowCustomConsumables && components.getCompound("minecraft:consumable") != null) { + ServerUtils.verbose("Item is consumable and not allowed."); + return false; + } + if (!config.allowCustomTools && components.getCompound("minecraft:tool") != null) { + ServerUtils.verbose("Item is custom tool and not allowed."); + return false; + } + + ServerUtils.verbose("Checking Entity data"); + + ReadWriteNBT entityData = components.getCompound("minecraft:entity_data"); + if (!new EntityDataCheck().passes(entityData)) { + ServerUtils.verbose("Entity Data Check Failed."); + return false; + } + + return true; + } + +} diff --git a/src/main/java/me/trouper/sentinel/server/functions/hotbar/nbt/EntityDataCheck.java b/src/main/java/me/trouper/sentinel/server/functions/hotbar/nbt/EntityDataCheck.java new file mode 100644 index 0000000..a2e035a --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/functions/hotbar/nbt/EntityDataCheck.java @@ -0,0 +1,40 @@ +package me.trouper.sentinel.server.functions.hotbar.nbt; + +import de.tr7zw.changeme.nbtapi.NBT; +import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT; +import me.trouper.sentinel.server.functions.hotbar.AbstractCheck; +import me.trouper.sentinel.server.functions.hotbar.items.ItemCheck; +import me.trouper.sentinel.utils.ServerUtils; +import org.bukkit.inventory.ItemStack; + +public class EntityDataCheck extends AbstractCheck { + @Override + public boolean passes(ReadWriteNBT entityData) { + if (entityData == null) { + ServerUtils.verbose("Entity Data check passed. There was no data."); + return true; + } + + ReadWriteNBT itemData = entityData.getCompound("Item"); + if (itemData != null) { + ServerUtils.verbose("Entity data holds an item"); + ItemStack heldItem = NBT.itemStackFromNBT(itemData); + if (heldItem != null && !new ItemCheck().passes(heldItem)) { + ServerUtils.verbose("Item contents failed check."); + return false; + } + } + + if (entityData.hasTag("DeathTime") && entityData.getInteger("DeathTime") < 1) { + ServerUtils.verbose("Death time check failed."); + return false; + } + if (entityData.hasTag("HurtTime") && entityData.getInteger("HurtTime") < 1) { + ServerUtils.verbose("Hurt time check failed."); + return false; + } + + ServerUtils.verbose("Entity Data check passed. There was no flagging."); + return true; + } +} diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/AbstractCheck.java b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/AbstractCheck.java deleted file mode 100644 index 97f49cc..0000000 --- a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/AbstractCheck.java +++ /dev/null @@ -1,10 +0,0 @@ -package me.trouper.sentinel.server.functions.itemchecks; - -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.utils.ServerUtils; - -import java.util.Arrays; - -public abstract class AbstractCheck { - public abstract boolean passes(T input); -} diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/ItemCheck.java b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/ItemCheck.java deleted file mode 100644 index 9e0e68d..0000000 --- a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/ItemCheck.java +++ /dev/null @@ -1,218 +0,0 @@ -package me.trouper.sentinel.server.functions.itemchecks; - -import de.tr7zw.changeme.nbtapi.NBT; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.config.NBTConfig; -import me.trouper.sentinel.utils.InventoryUtils; -import me.trouper.sentinel.utils.ServerUtils; -import org.bukkit.Material; -import org.bukkit.block.*; -import org.bukkit.entity.EntityType; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.BlockStateMeta; -import org.bukkit.inventory.meta.BundleMeta; -import org.bukkit.inventory.meta.ItemMeta; - -import java.util.Arrays; -import java.util.List; - -public class ItemCheck extends AbstractCheck { - - public List> checks; - - public ItemCheck() { - enchantmentCheck = new EnchantmentCheck(); - } - - @Override - public boolean passes(ItemStack item) { - try { - return scan(item); - } catch (Exception ex) { - Sentinel.getInstance().getLogger().warning("Caught an exception while handling an item check: " + Arrays.toString(ex.getStackTrace())); - return false; - } - } - - private boolean checksPass(ItemStack item) { - - } - - private boolean scan(ItemStack item) { - ServerUtils.verbose("Checking item: " + item.getType().name()); - NBTConfig config = Sentinel.getInstance().getDirector().io.nbtConfig; - - // No metadata? Nothing to check. - if (item.getItemMeta() == null) { - ServerUtils.verbose("Item passes because it has no metadata."); - return true; - } - ItemMeta meta = item.getItemMeta(); - - // Check for an inventory inside the item. - Inventory inv = InventoryUtils.getInventory(item); - if (inv != null) { - ServerUtils.verbose("Item contains an inventory: " + inv); - if (!new InventoryCheck().passes(inv)) { - ServerUtils.verbose("Item failed inventory check."); - return false; - } - } - - // NBT-based checks (e.g. custom consumables/tools). - var nbt = NBT.itemStackToNBT(item); - var components = nbt.getCompound("components"); - if (!config.allowCustomConsumables && components.getCompound("minecraft:consumable") != null) { - ServerUtils.verbose("Item is consumable and not allowed."); - return false; - } - if (!config.allowCustomTools && components.getCompound("minecraft:tool") != null) { - ServerUtils.verbose("Item is custom tool and not allowed."); - return false; - } - var entityData = components.getCompound("minecraft:entity_data"); - if (entityData != null) { - if (item.getType().name().contains("ITEM_FRAME")) { - var itemData = entityData.getCompound("Item"); - ItemStack heldItem = NBT.itemStackFromNBT(itemData); - if (heldItem != null && !new ItemCheck().passes(heldItem)) { - ServerUtils.verbose("Item frame contents failed check."); - return false; - } - } - if (isSpawnEgg(item)) { - if (entityData.hasTag("DeathTime") && entityData.getInteger("DeathTime") < 1) { - ServerUtils.verbose("Egg death time check failed."); - return false; - } - if (entityData.hasTag("Hurttime") && entityData.getInteger("HurtTime") < 1) { - ServerUtils.verbose("Egg hurt time check failed."); - return false; - } - } - } - - // Bundle check – recursively check the contained items. - if (item.getType().name().contains("_BUNDLE") && meta instanceof BundleMeta bm) { - for (ItemStack bundleItem : bm.getItems()) { - if (!passes(bundleItem)) return false; - } - } - - // Campfire check. - if (item.getType().name().contains("CAMPFIRE") && meta instanceof BlockStateMeta blockStateMeta) { - BlockState bs = blockStateMeta.getBlockState(); - if (bs instanceof Campfire campfire) { - for (int slot = 0; slot < 4; slot++) { - ItemStack campfireItem = campfire.getItem(slot); - if (campfireItem != null && !passes(campfireItem)) { - ServerUtils.verbose("Campfire item failed check."); - return false; - } - } - } - } - - // Lectern and Chiseled Bookshelf check (by validating their inventories). - if (item.getType().equals(Material.LECTERN) && meta instanceof BlockStateMeta blockStateMeta) { - BlockState bs = blockStateMeta.getBlockState(); - if (bs instanceof Lectern lectern) { - if (!new InventoryCheck().passes(lectern.getInventory())) { - ServerUtils.verbose("Lectern inventory failed check."); - return false; - } - } - } - if (item.getType().equals(Material.CHISELED_BOOKSHELF) && meta instanceof BlockStateMeta blockStateMeta) { - BlockState bs = blockStateMeta.getBlockState(); - if (bs instanceof ChiseledBookshelf bookshelf) { - if (!new InventoryCheck().passes(bookshelf.getInventory())) { - ServerUtils.verbose("Chiseled bookshelf inventory failed check."); - return false; - } - } - } - - // Spawner check. - if (item.getType().equals(Material.SPAWNER) && meta instanceof BlockStateMeta blockStateMeta) { - BlockState bs = blockStateMeta.getBlockState(); - if (bs instanceof CreatureSpawner spawner) { - if (spawner.getSpawnedEntity() != null) { - if (spawner.getSpawnedEntity().getEntityType().equals(EntityType.FALLING_BLOCK) || - spawner.getSpawnedEntity().getEntityType().equals(EntityType.COMMAND_BLOCK_MINECART)) { - ServerUtils.verbose("Spawner contains disallowed entity type."); - return false; - } - if (!new EntitySnapshotCheck().passes(spawner.getSpawnedEntity())) { - ServerUtils.verbose("Spawner entity snapshot check failed."); - return false; - } - } - } - } - - // Trial Spawner check. - if (item.getType() == Material.TRIAL_SPAWNER && meta instanceof BlockStateMeta blockStateMeta) { - BlockState bs = blockStateMeta.getBlockState(); - if (bs instanceof TrialSpawner trialSpawner) { - if (!new TrialSpawnerCheck().passes(trialSpawner)) return false; - } - } - - // Spawn egg checks. - if (isSpawnEgg(item)) { - if (!SpawnEggCheck.matches(item)) { - ServerUtils.verbose("Spawn egg match check failed."); - return false; - } - if (!new SpawnEggCheck().passes(item)) { - ServerUtils.verbose("Spawn egg check failed."); - return false; - } - } - - // Name, lore, potion, attribute and enchantment checks. - if (!config.allowName && meta.hasDisplayName()) { - ServerUtils.verbose("Custom names not allowed."); - return false; - } - if (!config.allowLore && meta.hasLore()) { - ServerUtils.verbose("Custom lore not allowed."); - return false; - } - if (!config.allowPotions && - (item.getType().equals(Material.POTION) || - item.getType().equals(Material.SPLASH_POTION) || - item.getType().equals(Material.LINGERING_POTION))) { - ServerUtils.verbose("Potions not allowed."); - return false; - } - if (!config.allowAttributes && meta.hasAttributeModifiers()) { - ServerUtils.verbose("Attribute modifiers not allowed."); - return false; - } - if (config.globalMaxEnchant != 0 && new EnchantmentCheck().hasIllegalEnchants(item)) { - ServerUtils.verbose("Illegal enchantments found."); - return false; - } - // Recursion check for use-remainder items. - if (meta.hasUseRemainder()) { - if (!config.allowRecursion) { - ServerUtils.verbose("Recursion not allowed."); - return false; - } - if (meta.getUseRemainder() != null && !passes(meta.getUseRemainder())) { - ServerUtils.verbose("Use remainder item failed check."); - return false; - } - } - - ServerUtils.verbose("Item passed all checks."); - return true; - } - - private boolean isSpawnEgg(ItemStack item) { - return item.getType().name().toLowerCase().contains("spawn_egg"); - } -} diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/SpawnEggCheck.java b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/SpawnEggCheck.java deleted file mode 100644 index 79d0ad5..0000000 --- a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/SpawnEggCheck.java +++ /dev/null @@ -1,31 +0,0 @@ -package me.trouper.sentinel.server.functions.itemchecks; - -import me.trouper.sentinel.utils.ServerUtils; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.entity.Entity; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.SpawnEggMeta; - -public class SpawnEggCheck extends AbstractCheck { - - @Override - public boolean passes(ItemStack item) { - ServerUtils.verbose("Running spawn egg checks on item: ",item.getType().name()); - if (item.hasItemMeta() && item.getItemMeta() instanceof SpawnEggMeta sem) { - if (sem.getSpawnedEntity() != null && !new EntitySnapshotCheck().passes(sem.getSpawnedEntity())) { - return false; - } - } - return true; - } - - public static boolean matches(ItemStack item) { - if (item.hasItemMeta() && item.getItemMeta() instanceof SpawnEggMeta sem) { - String eggEntityName = item.getType().name().replace("_SPAWN_EGG", ""); - return sem.getSpawnedEntity() != null && - sem.getSpawnedEntity().getEntityType().name().equals(eggEntityName); - } - return false; - } -} diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/TrialSpawnerCheck.java b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/TrialSpawnerCheck.java deleted file mode 100644 index 4b06e98..0000000 --- a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/TrialSpawnerCheck.java +++ /dev/null @@ -1,33 +0,0 @@ -package me.trouper.sentinel.server.functions.itemchecks; - -import me.trouper.sentinel.utils.ServerUtils; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.block.TrialSpawner; -import org.bukkit.block.spawner.SpawnerEntry; -import org.bukkit.entity.Entity; -import org.bukkit.spawner.TrialSpawnerConfiguration; - -public class TrialSpawnerCheck extends AbstractCheck { - - @Override - public boolean passes(TrialSpawner spawner) { - ServerUtils.verbose("Running trial spawner check."); - if (spawner.getNormalConfiguration() != null) { - TrialSpawnerConfiguration config = spawner.getNormalConfiguration(); - if (config.getSpawnedEntity() != null && !new EntitySnapshotCheck().passes(config.getSpawnedEntity())) { - ServerUtils.verbose("Trial Spawner failed check: Normal entity snapshot not allowed."); - return false; - } - } - if (spawner.getOminousConfiguration() != null) { - TrialSpawnerConfiguration config = spawner.getOminousConfiguration(); - if (config.getSpawnedEntity() != null && !new EntitySnapshotCheck().passes(config.getSpawnedEntity())) { - ServerUtils.verbose("Trial Spawner failed check: Ominous entity snapshot not allowed."); - return false; - } - } - return true; - } -} - diff --git a/src/main/java/me/trouper/sentinel/startup/drm/Loader.java b/src/main/java/me/trouper/sentinel/startup/drm/Loader.java index 2500200..832232a 100644 --- a/src/main/java/me/trouper/sentinel/startup/drm/Loader.java +++ b/src/main/java/me/trouper/sentinel/startup/drm/Loader.java @@ -4,7 +4,6 @@ import com.github.retrooper.packetevents.PacketEvents; import com.github.retrooper.packetevents.event.PacketListenerPriority; import io.github.itzispyder.pdk.utils.SchedulerUtils; import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.config.MainConfig; import me.trouper.sentinel.server.commands.*; import me.trouper.sentinel.server.events.admin.AntiBanEvents; import me.trouper.sentinel.server.events.admin.BlockDisplayHideEvent; @@ -28,7 +27,7 @@ import me.trouper.sentinel.server.events.violations.entities.CommandMinecartPlac import me.trouper.sentinel.server.events.violations.entities.CommandMinecartUse; import me.trouper.sentinel.server.functions.chatfilter.profanity.ProfanityFilter; import me.trouper.sentinel.server.functions.chatfilter.spam.SpamFilter; -import me.trouper.sentinel.server.functions.itemchecks.RateLimitCheck; +import me.trouper.sentinel.server.functions.hotbar.items.RateLimitCheck; import me.trouper.sentinel.utils.Text; import org.bukkit.Bukkit; @@ -184,8 +183,8 @@ public final class Loader { Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), SpamFilter::decayHeat,0, 20); Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), ProfanityFilter::decayScore,0,1200); Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), WandEvents::handleDisplay,0,1); - Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), RateLimitCheck::decayData,0,1200); - Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), RateLimitCheck::decayItems,0,200); + Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), new RateLimitCheck()::decayData,0,1200); + Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), new RateLimitCheck()::decayItems,0,200); if (Sentinel.getInstance().getDirector().io.mainConfig.backdoorDetection.enabled) Sentinel.getInstance().getDirector().backdoorDetection.init();