From 9900516b10a446af596a57ba2d879b5c4b172dea Mon Sep 17 00:00:00 2001 From: Ggafrik <906823881@qq.com> Date: Thu, 3 Jul 2025 01:48:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- excel/enemy.xlsx | Bin 0 -> 8945 bytes excel/equipment.xlsx | Bin 8698 -> 8752 bytes excel/excel2json.py | 90 ------ excel/item.xlsx | Bin 0 -> 87589 bytes excel/level.xlsx | Bin 0 -> 9397 bytes excel/player.xlsx | Bin 0 -> 8637 bytes export.py | 5 +- src/ReplicatedStorage/Json/Enemy.json | 5 + src/ReplicatedStorage/Json/Equipment.json | 2 +- src/ReplicatedStorage/Json/ItemProp.json | 4 + src/ReplicatedStorage/Json/Level.json | 22 ++ src/ReplicatedStorage/Json/PlayerLv.json | 12 + src/ReplicatedStorage/Tools/Utils.luau | 48 ++- src/Server/DataManager.server.luau | 303 ------------------ src/Server/Proxy/PlayerInfoProxy.luau | 25 -- src/Server/ServerMain/init.server.luau | 68 ++-- src/ServerStorage/Modules/ArmorLib/init.luau | 258 +++++++-------- .../Proxy/ArchiveProxy.luau | 35 +- .../Proxy/EquipmentProxy.luau | 43 +-- src/ServerStorage/Proxy/ItemProxy.luau | 31 ++ src/ServerStorage/Proxy/LevelProxy.luau | 125 ++++++++ src/ServerStorage/Proxy/MobAIProxy.luau | 4 + src/ServerStorage/Proxy/PlayerInfoProxy.luau | 172 ++++++++++ .../Proxy/PlayerLoopProxy/PlayerAI.luau | 0 .../Proxy/PlayerLoopProxy/init.luau | 6 + 25 files changed, 628 insertions(+), 630 deletions(-) create mode 100644 excel/enemy.xlsx delete mode 100644 excel/excel2json.py create mode 100644 excel/item.xlsx create mode 100644 excel/level.xlsx create mode 100644 excel/player.xlsx create mode 100644 src/ReplicatedStorage/Json/Enemy.json create mode 100644 src/ReplicatedStorage/Json/ItemProp.json create mode 100644 src/ReplicatedStorage/Json/Level.json create mode 100644 src/ReplicatedStorage/Json/PlayerLv.json delete mode 100644 src/Server/DataManager.server.luau delete mode 100644 src/Server/Proxy/PlayerInfoProxy.luau rename src/{Server => ServerStorage}/Proxy/ArchiveProxy.luau (81%) rename src/{Server => ServerStorage}/Proxy/EquipmentProxy.luau (73%) create mode 100644 src/ServerStorage/Proxy/ItemProxy.luau create mode 100644 src/ServerStorage/Proxy/LevelProxy.luau create mode 100644 src/ServerStorage/Proxy/MobAIProxy.luau create mode 100644 src/ServerStorage/Proxy/PlayerInfoProxy.luau create mode 100644 src/ServerStorage/Proxy/PlayerLoopProxy/PlayerAI.luau create mode 100644 src/ServerStorage/Proxy/PlayerLoopProxy/init.luau diff --git a/excel/enemy.xlsx b/excel/enemy.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..212ea3b043f4a5b59866bc23d213070971038d71 GIT binary patch literal 8945 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 PT;0?`ID$V%?zi+8k>wEZ delta 2191 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 YfHx~J1v3Dbw(>Hh02AoyAW-W90GL*Y=l}o! literal 0 HcmV?d00001 diff --git a/excel/level.xlsx b/excel/level.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..da1b8b4b75006e8a26ea5093dba60f51a735bc07 GIT binary patch literal 9397 zcmeHN1y>x~(jDAouwcQ01$TnG4i;QPaCdhLgS%_+5FCO-a2X&t1PGoG+#P~^lf3)Z zy*KZEzu>(yYn|>{-Mf0GPwm>(RjmSogU16P0+0a!042cqFvs#03;+-h4*=i-kYV*C z9PC}q>|G7jy&TP43|KtvY$*!hVd?V#u+a1WH~x$7Kt=M1QZE~})RpX=#0IncQk^I= z_d)O=4#P{~uD+y!G84UAE33!ch#%PE`S{knwK(HTJ{%{bmUZ@ajltmqZK~)|L69zu zS0ucw{e$~7oll4oopp7N^6*GR*@+AdW6ZMv=}t|(s$7!mV)7NlmiPq1f!}hz3~B;b z2U^g}bW8ali`{EVnk&E;!|$iDzfI!KSX({!d3E-USq?sm%4dnd{Z+9FrMB90W7QVt z+cZ#z3w<-^>}wO)6*h4EN6h81Rm8RqUNS{4;=1>d=ViJB@ivT|zWTo2o%FF$=PI|Y zJouJ|%zC+^L6pH$^8gi_sCt4*4*PMxJ`wyvg^}YPisc_P>$s$St0bnaxpDEPN`zE) zOl!r1xsu9!ybQtq?;u0XFS?WuOh|q0OD2&v;H9n~@h*Wot!G{0*h5{E)P7+HYmglQ zpZ$sNdLf4=sXGB24~PK3<0BkEi&UpOIQsth14X=ophww>ck;%G65g` z?}itb1!Hz0l$T$*Dxz@k1fDf|RD#oPo!pQZXq{7~ohsJ*fbO#wvzHk%@?P}rALAIx zn@jUSL#xlEr%xnnvBz1oh*5BhNx=j{89}-OiaIOC*HtjH;%Wz#;F>1hyxpYntbn=H zk{t|@a6X0osSJFGi?PK*l`q7W^5TYAL(P)Us>V3inV-tb(A2i;Ofstz_uiLTDI20n z#f5XtF)lMmlXnrIThDbelH=9Ki_%*TIUWj*A}?KmD%5|HWOC5^dKF5Lb|{6gpm)a8 zmes?-*~Zwx!RDuKRiqd=WU=9akLV6WC|1?jg1E$JG=v*um+{;Td~n}8N8`2w%o~$b zH13ZC!0}w8igTDWJdNw$g|-CK%l9}ymCQ+zQG(*vJyHZC@58-1A}6ZbB9_Hcr>NuP z7@P_6=}IQtj`q^ZbK>_xv2D=co$>P}_o!S+rEpV$Zj`90y~C=IXiLK3ltR=Y*$SKi z*Hr%8y~dpAW71jFeBU^*HVdEGGcjJG5jSQk9eJ*KEfiMu!qmwth?!&+Z>XY_W?*0N zJADz20?4rC3xn_CiBhOWX)kKzQ960Rqvu`Tt8-@Hj)JM? z0>7->TGW=+heZIwEA=iddTByDkg$Ut^JuuJ(YT-S*Ya@0Ot58-7hR8*YPz)0zjGQU zBYZ9?Y(ZnTbB75PVTkOy_^6W_i@`bLU6)CJZjzKP)CI{ zzqs(svF(n)^DO**FbNsConYIKUY`4`5*;6Qm@JI_+z&bYu&=?IH(h7i`&6bTAqUet zj#|vZ$J%tFDibGbiQb1K-^$@hziG~KH-pAsL`_5#zC5sm4YSL{l^J6M&1#~2E?XI9 zYK7 zs8Ce|^|4`pM0*Hh^T=|?2QIj>QtfN(VIdFIKVPJ!wLV9gPmU zUvXo8g~Pq*W-N`wf%nF>JrY207(E+>LpDCBBny^7z{T0$-77yp!_0KSZ4pj{Fy-RC z*u18vAqp!jp^`jrnF4cJkFmLNSi4L-9frowk@}6m+c#?amBIB*O%$2W&A&gkNGx6= zz76&wpFAT%krTx50!(htP%>V3OI;$Lw%?g6dX3q17v7?OCoKn&m2nICf?X-XATs0u z&l6p~7i6?)9Id;G{}_JWxp>Mna(-mKF*G>43SH^{beXSBtc>Ug0012U06+*mcaYK!~QdlW+yGiE>d8Fj~FkWVqdX0x~Q1+>u4D0EVq>n+5``>qar@i3E zD>f;npC0H>nR*f<8?hOlvpCO^@2B2D5-)rNN#$FM??Qwv<;p&GJXsg@Cs zUtpOB;@*ouc+RfXr!U(t zvZ03>(j3EmN>J`9TM!r{IH#T-V?d62H{j4;u z(Rx^BF?nlkyQnZ%V5P`}*W-N3DP2Hms`jlwg{y6DEDl3Js-Z>lQTy2`24leFN#pkR zlc(>b!ct=G+V3vfeGOM`t~@*a=l2dw0%O3%zG37S>pwQPmkPTyD{@C`&GnRHIb1(+!AU%?4RAioPM4c!tV`0q9eZkS zzEyd~P+o*p_Nf9xJME2G?knz*_Hju2;HQe0{MDzQ#8NUEVD=J=Dml44PWj2%&d!Cp z9Za?~a`{R*K0vJ{*1HLtn+h9m?mS!L^hpWG|JZ7>yyUY0QLTsBgRN67$;oYqe(y}L zZp4mG@>H-F7;yv`;%MSGW>?bvmd@l)91VFr+s#=p6hbOj6{8)U^jVk|$?vs0kSM&3 zB8vO!P_}vk{$*1CiwK*s(u@0?egi`?W;l>VJ5wSOU)|=H@KBjzGMx`YJ7+NtXikHt ztxkZyVYPLwjsVw417`LOb#e8zMqB0fX8tx1iZ zsW-Cct^=8*kYJ)hmrsdfh2?qB)3$d?x%ny(IHkAhX>w$=yg;~AlhYA4&hcpG(PQ}} zu1>!A`%@-VgIWlzn+gIQZkS%((KJryiDT{|rbGr$Yt!j4;M_31yQ3%he12Z$ateMy zHk~f!qub}VIYA|)9mnzW4M7`_G8^s2bULS5mo+^kOXu6fF)f2)R>klNZ+^c$(%ayL zO8e0r5dV)w9!PM9Se$<73^0oCNvs3kJ>8bbFHin8agBDP_OM~YPZ*;7fYAi*6z}vW zA?0^sFWTYE%!of+$|`B(7V*BN(M3l6pE{aT%o_y^n_ViE#W0ijAcc*B!$^D&}=r%!G(>dt`v zO!{F|ENfze7c+15mOEcb`Gf-T1#G$7@^2aUQA2jD3@$Q82eXv?M2>q82RdO@YGX*a zkk%=duGIis8C|k2EUn{tbB7lpu;oFgpr|AN538j_o}&RKQ7*jkB5>8BEdc>&BBavT zQqX-q;hMf8ojBiLyGuR1AMIBih&>S^pUWvXO}xV9DY&rYjVCG$)ASTL4jc7eZvzxr1*RVnH?yI8&P{&h=smfFkc!FtF`hf+e*}$#vPbT#32=YW>=GBK79}(}; zL<|kyhvx}JbJ+}`r5{()4h^m+MKehbenfd?fN|=$L&^qBlAA1dHr6IBaIMm7zr9%u zfIe*9{AhanO6Uj6(K%J_-gR21|NUv4h~XC5fG*;C|JAtR!^2%a*6Q8G2xrulJ$QiM zR}$w_&f@h_%ODP6W~d06g@XlqB#@m+1-*m03JLqgQ%|4JQjED?b7XSUJXW5y?$d3)y zzRl~PX_FbAU14F)gkxO_Yf6!x8RTy;x~R$X`mrkF7lGuIz(v{Iin@qc5=WSfT$ADK zZaf$J4=qXEo()B-uy(cY?$fJqa5GSxtuB>Oaeb~eJXWKBxv^x{ zgD7IYNkVl*iABGhafk#@veTiDd>B%(n>-Agpq!dPlZKR@ar7;63|o@{uFN6aOMD8H zavxlRM6Nr1u%)c@keqqPop10EG_$W4dSZL<`q}QfS$igpiUpevM;fTg( zuFdyC+`yU>;@%>xReoW*W$Y(SF9c+ajBD1dc1cetk+4)JL21ZZvRxry*C)_}gX0~K zuXEy{?VzR-j+lfcxZrNM$Uf`2$QD2wFsiK1oTVi+Jg#&{AK6G(JQK36q`fL^@pPmf zc4wWh?|l&8*sjqzY~wkcRdTPo9JGO*y0nwE%*mi8CcqhWduF=sMm+{w>jwT(Y+B#D1Gj*;P*km-v020zEz0_LeefiU@y!KAvwby zR3#8bCzF=;g~ap=MSe<_oH%1Ip`z2voLi$Ijm=hrl9@a@iCTXWdE(K}VUPyA1hg-r zY6j0Oce6c~YJgo{^YG04hv=8Lju}jO?#=qURQzFZA0i;9PaX`C%@Uj@Wdz6IswC;O zLf#-}^CeS1)xr!=!{E{&E@+z*uh^H;fUtZ&QAHn=94wiF~ZHY%*1 zOk?p5*;EY6&Ad(lHGSbZR^9tzQyC)OFou)%snvmv(sE{a4Ba~Mq!Ar7%A%`it<_91 zK?OVvUo?-ck^X96;a}6H-QXOoUCpSmULN=K_6nKDAO7oPSkfvDzJ4)I2ahQ-f#1!7 z6AQ_4q#!{*F|aDzOq%}?mv#I0rBFD-D@H=~JMfW)u2|E{9YnE09%SoRomv5?xKZIr zy72*)L_I(}1N^so>OVfyrJ4E-yW907GN`>(FNde&x1#_e@go+Q_^dIZ~(vz5&(elFVS*w^|Cc{`BgK~7_pxg z#an)Mf>pTC=1+|&XWC|+sapn*qZ-xcZXB2Na?2=&u2QWp7Q?V7(yt#}E%u$W`J2qQ zvT*Sc+KiT~v6e02r%yiz7lO(dOH8Bh;jo@DaHep!#GbUB&14v>6H&SJq!h8QOrnz0 zvKmUJEI4^L=FdZpTzUxvvKQ+oyeErf5kX5d%>1P$b;!7xE1y}4ZUu+hNo zzOG6^)SSR;ze9>xs@yi}$S6;xga&71)3`Dp zP$++G@Z2PCD3RX6w32$A$Lv1p5U_5Ve8wqYdKBd|?-sP7S=5ug$qsgE7&?r@mi4eb z`4ZJnUeGWPHxE>K@X%I&;&DCh86&H6x(u|id0u*;mnAx7I4N!AjzRfFb5vB>DACA} zC>4X6r>E6Z6Z(;;Rl=;TdK0Pmcs6*2)6s$^*qyLEgW>KqWIUqL4@K>Js zds%+0TGz`JS~*#QcFXYo;Ett{vze)ytFx88#V-ogt1E(f*$6wZF3H7}P#fglp(ixL zAqB5>hvW`pHjI8=%bcOjIpo21BLh=;lkJM(!5dM)Ap|H2+&?jIjv~W&me%fY>f5eN z;eJ4GAA%`|@cmOzY6K+((;B52Gt%LqEmNg9MbWr;=O~fW>ojC-mk}C0WE8wq6eMX? zS{vjD3aVOGgB6vVb4*NpX0S&w#$zs^Y(Q-&ET9gn%A&Bh# zJEKa`If9vnVvq!h+?*0sF{@QgPB5*7GpD*eW&E;ahH?AYf-Q2mK8a!iCsz{x@$*j1 z6^^K_AHIStX$+fWPsGFaCXOt<@)f)>D%mU*8Z8wXaAFeO#yBb|d|RqV6R={>9;{Y; z=1{0bh`xs`8oO#OwR^5?cx#`to;A&l$F0NiR1Wpd7Q8&qrQM3-%C?*7ul|5G%|!y9 zoA5>-|4?J3_=JZ6Tzu_6=R)(y$b;k*J?r@xgZzGV=>8v@Cq_+I-qO%j(t;-KKxi$@ z)WJl>*}>6;)x^Qs?2nG-|4Lv`_w`MDsT{(F7rLr=hgEbmLD0xY@;V|@sHLxuD8@n2 zXE3}zzH#{Sh`EN+d|=RLe8*$_qg5+^DZ@3a$%w8+xHLRMo)k04z;xTJ4Kt0tCe~)C zTvq}d`+Ab0vqN60SJG~6Xa<8&D;PsiPLDPd{UFDNa6ofk%!$M+?DA_BPo#ihCM>VM z?=h^S0tA-PQQvvc2?;$bAzVrJxwPq-RT&ut0vHv~(tr>m*_Dxbn&3=1GeoH6CVsQ6D!u0z|*AoHW;8N;JC zZ3&4S^G8s$&9F)A18(KNxv4P^By5C1AYZ0kNRo?E-GV zQB<6JI8R{&GD7Jk;1*(=a5J4}Z|F@P-RF@B>0=~<*x8s1@-w@oH;fd%$Y-vlp9N&K zT^-hXyTVQBDe#lPx=KLrm4mKiE; z|NMf;zgO?y+kbdnU=2aJHEHEaDjPC#iI9z) z#bF(ym7zAga?tySBapsTRh_RyqGM%qz>OJ_c{^JUf1|?%9$2O8z2{=s!4oH#}Jsscgr(eEcStFqtSfRFP&rM9S zP^DmWyuVs9oGYjHP=qrq=-tR@t7xy<4|AFTr_vd$O?3Iw8`5K1 zkJXVKaewIaH~nzf!JD06fon_v;N}Jmp#C>lHfi%Q9U)`wDH3&ekgzm%vvhFh<@t5} z9~}RSG5D9ESEf8u1M}fU!WA#0MovC1Cjn(tyrmU87_@>yl$Hpa;)|H6mwVV~fLc`H zXbQnyL6>8YVSF_VWerFT5$;!IzAF_!K&uNjBncX8ol)V8np|aYm)BY4gyw)-2aejM3NF!lCz2?rjgr zRRFhY_J{_fAn~cdl)^AeJ|Y;@D0ncQ<2xXN1Fjg^9}SD8d;b-gq5cUH)23w9O(Z~i zkPsq7&Ww)(ueY$WXszOleWVGuiTo*+=T9$S)xc)Gp5-XMkEy3^a~X7$o0AH-;ekZjj73V zPh0Vc*=xSihWP`7mJN%D?e|ZxzxneGnDuc`MDZJ@FOhGlXmv*?vGhC%baR*}yd>izr9rQCWE8(*%`9R$(e4^@ z`;5`r?SP8egOWaIamwo`r_l}?G>FQio5XO7ON6}NZ2w;4i)&N^LGUEciI4bl_Ju62 z6laZQ+Xw^OFw1_F1?%XL9n%xho9!iB>A8=tLQrs|Mfr%SQrfbJb9C)9pV$B=OEoU7 zv??;Q(^Ew)6XQr3^XgLSJ&u*bHx2f0@?~V65z}CIvc6d9d4}qeW?Ix(_J({(EwUx) zN1g>*TNLi_g_=9+=-{=cs%3OuE(Cngi>PxZ43jo#Ilmr(VS>AVoNa`9lf>~8Eb;>5 zpb=V0O{&M1LeoTc()d#bD@tE{YN@Mz1BwrzFha-&=g6TGGgM)GXlO%~7;bNd&e_PJ zt^<>WITK{LtgR|L@zEz7_V>#O6QwIC^~N|lg}J0l{8EW^hWz)4nOZBsNU$&%hiRLv zq+A=d$*%dQ^4GaEXh!F<)C=MGM&$?CVuBqbN^une!n_(Ovt^ARw78)@D901HI=lKM z6_gP}q4Z8|BI!=k^~=dC&HBr|4ISJo4OmqLB6h|Jkwrn;40BNM&CE~EuBZv)f!e3h z^Cv0OWS9Q1&xLBFisb~@r(ulLpHvTZ;JnCi|0f_qCDPIlk-xMfK|uw;MnMANUy0+d zu=sZpK|yjlB&`0sTcyS`l>t6r*PZJqKJT|)WJF&)cp0JEdxY4djT{hGh6h1ME7Y9r zhPt0rc+s3Aec=u4m1KCbsM#OQt`4sdY{=ZV7!G;FgURkUFW7$n5d-Mxw* zcL=iFN!lb*M(*d5if)~xLmR&4A#*Xt(F6H;fBXn;wK+QcaRs^3{|p)IwRcfh z$Z%;x9tS9pPyDfmy4zSf;aG0baqZ6lqPyDC+(Jw zji=Es)8~cXmUsKJ72GGjE0^|}gm*>K{04lW!&Q&DT_VI04zzCJe(ls+=d_~|B9&wkpgayNkKpH3o?dEk#K5%1-4i4{-6Q|-g23mi z!)%Oa;rl7U^hg-atBy$1SQ*?5P~EIaKkd5ILCD#KhR(Ir+&WeEmQ!0fsDBriC{s%S zO2y6sVm^FXmwQmAv~AyGw60E?q1mbNiF>SKLQ!qTVpLni&aKrk zqU;*yq-;~JiS;wJ{L|?X@shDht6#*~fhg+CrNzhARzK>EM28Eole_iQncA@3@JZA; zOGpNYZhU*UQCvix_t z-%I%Wl9ski5XOi1#P}r?CE|dPQiZt_Tbj6LnlJ49H~}PGt^&Om;5a#&SY*N-mL|y7rOkA_OdQ z-I(!u<^KD2xyK#3*;^SP;k00XM(=RS+-I-A{pWMZ&1|~hW=L1TNmhpAB@!6_gv>L^ zkoyrx$`L~K>&W~YG9EUTj+VT?ufNd()thpL2$Oabe!ijq4g)o_d?DB+ZFXR@Pi?kY zB6vJ2>{wSICtI9KB_C?&P05!}?L}D&ApnPTqUC}Pg7ts z@A@awt@xjx$}TuRQho0p`9M{7(baz8(L&!{x^ct zx|=iWg_+!F*RXrFYG<^&t#!cIF1y!i`IHCt4xySp!sdEm=u1|IyMnUYt0Dy-+qYkS zL{Fag)JO|~h&NDXnpiU%wqMw9qv5uct=5~$G?462QIq3}Kdw?jH$I5AnxrheclSKA z{*Wl>zCjct&#Lq=Vm?oQxyMM}|0NNbxPwqf!8sQcH+;v|5Ro}C{8lwkavuyE>Oobn zi>DUETBBb&)dUD;_A0vbv`^(Pz!2f66`?;>V&OpzEAJ_NCW6gl-9?fl(Q2gHf`ji! z(mVmmBkvzd)D9HuB?kPyUFsLlNhBn6e`JmB3ac3i3%;UiMFFZn6r0cC{spR)xhw( zFfY?3jSb79^B>0v+K=AJ*so$89bQX~yDvA~g=1uh|I=lMhL0#!X{N%>OqZt6qguc7 z{0tI|q}XQ{EqO+dFL>ZbjJbQK={-SLKNBU5w`qq!m}`S4Q^wcVmxF{Wmx%EPu_sQE zLt+7P#2<1Xr%P?a#1vUCC26glt@&e!`0uOZb#qr^0Y&fm_`iIQzW}zvrnAWB6<+Q8 zncRn9o_rIg>(@=jhPj`?zi6H*1Zc4^21O{0dpN-#>F>7sW9NK1Pf)jG_Ng@psU?0p z&OU@W(Lq{*I^i1gk}0oFGmg8+Uoo0!!CYXJ!urA5BW6`XB74Or77nBuwjPSaBjaOF zfEtbiTc@FO=JUe45)u!R_d)b_43hox*c&Jy+_cXI3Fl;g!2-W2nh$Z!%fZu`)8@8!?kW9Zs}z=V)J< zS(VpUVWn7fpt&Er0T^~p-j@<$Unimwj$)npUpriGq$glSmVS4^_Vtgg15==CDL<~^ zEdC1}0SFmGy9Xm)Z4xgk5Ek2Jflt^U6LG~Pwt{NhGm=YXZM3Lyn%*usd?n{MAlFAj z6PZe?cNJjmW@eOlHG@iy5NbkXANC`%#qSK6R@G#EWTi0Pukykh-^_S+_}H$B^`xlH z$Ax*!i}!OQ_*!Q3d#xUTQ1chW#z+o)M(EwFnh+$ z9#Bp8>D}1na^GwUH^Va9!O;@soTV3`^ec>sEWFh;7tx<(-)6FjMZrthdV0ZkSxaTS z5VCUGvcBSlu@qzvVtBS58oc@*na~aykm}$X!b4dmSo7gw`+Sn?$(vVhxhXhvfBUJd z_GYE+-8n*$QTxg|V6ePcst1q9IlZwNKU!t%GViber75bZfY@*D4b4ou6O}2cfUeM5V5ajz|<~g(}h;ep$!DHOu%;k-iGAJCk zDOq@s202>51dv7jX;d+-|#T7m~>+DBrbJC_(_WXNe!7wu74UmUj z(mx{K#?;NyLes;|*2(%`0GO7xL*vK8m<}xP=35lWDrJZ;6OWb3@8HQo%cx7?kF#8u zP1`*7v{jwvkz6m}{cn0jiHPHg9Y2lU1EU;8}+lMxZKf*MSJ3C487UcrCc94cRoN~N>*96UK?Wk>f zO{6EJC8bk4uTUP*tQy!+deD{WZWi;KQj8{NWX>O=LpH{|maYiNkU^ z%^;C93j5a>koH4Aj)gUR+)8uh+EVSLA~fAbP99!?Y7S@oN0~a|u0o%CQ`1Tewr#ev zODXib(MBCM?~TPmJ}GVm*m71<-k6ZBMkKMkSSfi!beHERc*vD3UEjR%H(#WC_ zr!|Nsm|bponiO9h_T_)buJ^+F;Vm|l(||!YINH^Muz(Hnt_aMbKv$+kU_ zmsyure8ZrZc7Px6Z0m>ks&<9wP81T}bUp4F7$B*UO-W-eu~i<;_CY13l(@hh6#0*_ z*J;yCn1zfzVl)5%_}ADob#eJ$=pm=L=I3Lw@yiz6ujsyCY_`!%Ktp-05`PTtO^ZelZ@9UzsiC)2Vcrh#|NaWGEcp zfEoR69$xi|;bly{w|}_mXxaM=r}!-f(5^@Irl7Vx8bP{4C6^1%>j2L8)Dc0pT($hR zA7=~YWf`nSgSMJ-VxUaSP*>a;na0&ub?43elx7ReG3O_V&OJdvXNQQ6l*+^=hU{il zbN|w9Sv0KS_^!T@zJC6-p%m~@0Z6*|1~B!m7Y!UOzFtY=LE#LsjsSnS{3Hn*R~K0o zCnuPxLgdXFHsHLFI&GcWCMDPC@b{A5zYTx&=}3_LrNDRF z@OGKwPt#5$nY&f=xNZD*N%&7w0AKx6&tg!g|h@ekGTwwK$|$)8>x zAPYFix4A8(+;(uA#{TKxJK1jsf0EkUrnh;(pQh_b;S>4a?zdUNZ4ZA>sDIi601!$5 q;2-Jrw)x-p#J`%qp!tjWA3LPF3I=j@0RUX&Cj^ Variables -local PlayerDataFolder = ReplicatedStorage.PlayerData +local PlayerDataFolder = ReplicatedStorage:WaitForChild("PlayerData") --> Constants @@ -38,15 +38,15 @@ function Utils:GenUniqueId(t: table) return min_id end -function Utils:GetJsonIdData(JsonName: string, id: number) - local JsonData = require(ReplicatedStorage.Json[JsonName]) - for _, item in ipairs(JsonData) do - if item.id == id then - return item - end - end - return nil -- 没找到对应id -end +-- function Utils:GetJsonIdData(JsonName: string, id: number) +-- local JsonData = require(ReplicatedStorage.Json[JsonName]) +-- for _, item in ipairs(JsonData) do +-- if item.id == id then +-- return item +-- end +-- end +-- return nil -- 没找到对应id +-- end function Utils:GetIdDataFromJson(JsonData: table, id: number) -- 遍历JsonData,查找id字段等于目标id的项 @@ -58,6 +58,34 @@ function Utils:GetIdDataFromJson(JsonData: table, id: number) return nil -- 没有找到对应id end +function Utils:GetMaxIdFromJson(JsonData: table) + local maxId = 0 + for _, item in ipairs(JsonData) do + if item.id > maxId then + maxId = item.id + end + end + return maxId +end + +-- 将字符串数组转化成真正的数组表,"[1,10,2,10]" -> {1,10,2,10} +function Utils:StringArrayToTable(StringArray: string) + if type(StringArray) ~= "string" then return {} end + -- 去除首尾的中括号 + local content = StringArray:match("%[(.*)%]") + if not content or content == "" then return {} end + local result = {} + for value in string.gmatch(content, "[^,]+") do + local num = tonumber(value) + if num then + table.insert(result, num) + else + table.insert(result, value) + end + end + return result +end + -------------------------------------------------------------------------------- return Utils \ No newline at end of file diff --git a/src/Server/DataManager.server.luau b/src/Server/DataManager.server.luau deleted file mode 100644 index f074c4b..0000000 --- a/src/Server/DataManager.server.luau +++ /dev/null @@ -1,303 +0,0 @@ ---[[ - Evercyan @ March 2023 - DataManager - - DataManager handles all user data, like Levels, Tools, Armor, and even misc values that save, such as active armor. - I would avoid messing around with the code here unless if you know what you're doing. Always make sure a change works properly - before shipping the update out to players to avoid data loss (scary!!). - - "PlayerData" is a reference to the Configuration under ReplicatedStorage that loads during runtime. Make sure to yield for this to exist. - This Configuration houses all "pData" Configurations. These are individual player's data that houses many ValueBase objects, - such as NumberValues, to easily access on the client & server. - Like any instance, trying to change the data on the client will not replicate to the server. - - While a solution like ProfileService & ReplicaService is recommended to avoid instances and lots of FindFirstChild calls, I still believe - that this is the best solution for beginners due to the ease of use, and you're relying on Roblox as the source of truth. - This makes it easier to edit values in run mode, especially if you aren't an experienced programmer. - - IMPORTANT NOTE: ---- - 'leaderstats' is a folder games use to make stats appear on the player list in-game. Please note that this does exist on the client-side, - but attempting to change stats here will do nothing. All player data is actually stored under ReplicatedStorage.PlayerData. - - If you're using third-party scripts that rely on leaderstats to be set to, you may need to alter the code to work with pData configs. -]] - --- 主要加载和保存数据 --- 生成对应实例,并把数据加载进去 - ---> Services -local CollectionService = game:GetService("CollectionService") -local ReplicatedStorage = game:GetService("ReplicatedStorage") -local DataStoreService = game:GetService("DataStoreService") -local ServerStorage = game:GetService("ServerStorage") -local RunService = game:GetService("RunService") -local Players = game:GetService("Players") - ---> Dependencies -local GameConfig = require(ReplicatedStorage.Data.GameConfig) -local ContentLibrary = require(ReplicatedStorage.Modules.ContentLibrary) - ---> Variables -local UserData = DataStoreService:GetDataStore("UserData") -local SameKeyCooldown = {} - --------------------------------------------------------------------------------- - -local PlayerData = Instance.new("Configuration") -PlayerData.Name = "PlayerData" -PlayerData.Parent = ReplicatedStorage - -local _warn = warn -local function warn(warning: string) - _warn("DataManager Failure: ".. warning) -end - --- Clamp the passed ValueObject (must be int/number) to the given bounds. -local function ClampStat(ValueObject: IntValue, Min: number?, Max: number?) - ValueObject.Changed:Connect(function() - ValueObject.Value = math.clamp(ValueObject.Value, Min or -math.huge, Max or math.huge) - end) -end - --- Create and return a stat value object, used for player stats --- ClampInfo is an optional table for clamping it to a min/max. If you don't need it, simply don't include it. -local function CreateStat(ClassName: string, Name: string, DefaultValue, ClampInfo) - local Stat = Instance.new(ClassName) - Stat.Name = Name - Stat.Value = DefaultValue - if ClampInfo and (Stat:IsA("NumberValue") or Stat:IsA("IntValue")) then - ClampStat(Stat, unpack(ClampInfo)) - end - return Stat -end - --- 这里生成实例,在Configuration里生成一个文件夹,然后生成对应的ValueObject -local function CreateDataFolder(Player): Instance - local pData = Instance.new("Configuration") - pData.Name = Player.UserId - - -- Stats - local Stats = Instance.new("Folder") - Stats.Name = "Stats" - Stats.Parent = pData - - local Level = CreateStat("NumberValue", "Level", 1, {1, GameConfig.MaxLevel}) - Level.Parent = Stats - local XP = CreateStat("NumberValue", "XP", 0, {0}) - XP.Parent = Stats - local Gold = CreateStat("NumberValue", "Gold", 0, {0}) - Gold.Parent = Stats - - -- Items - local Items = Instance.new("Folder") - Items.Name = "Items" - Items.Parent = pData - - local Tool = Instance.new("Folder") - Tool.Name = "Tool" - Tool.Parent = Items - - local Armor = Instance.new("Folder") - Armor.Name = "Armor" - Armor.Parent = Items - - -- Preferences / Misc - local ActiveArmor = CreateStat("StringValue", "ActiveArmor", "") - ActiveArmor.Parent = pData - - local Hotbar = Instance.new("Folder") - Hotbar.Name = "Hotbar" - Hotbar.Parent = pData - - for n = 1, 9 do - local ValueObject = CreateStat("StringValue", tostring(n), "") - ValueObject.Parent = Hotbar - end - - return pData -end - --- Unloads loaded user data table into their game session --- 这里把玩家数据加载到实例中 -local function UnloadData(Player: Player, Data: any, pData: Instance) - -- Stats - local Stats = pData:FindFirstChild("Stats") - for StatName, StatValue in Data.Stats do - local Stat = Stats:FindFirstChild(StatName) - if Stat then - Stat.Value = StatValue - end - end - - local Items = pData:FindFirstChild("Items") - - -- Tool - local ToolFolder = Items:FindFirstChild("Tool") - for _, ItemName in Data.Items.Tool do - local Tool = ContentLibrary.Tool[ItemName] - if Tool then - Tool.Instance:Clone().Parent = Player:WaitForChild("StarterGear") - Tool.Instance:Clone().Parent = Player:WaitForChild("Backpack") - CreateStat("BoolValue", ItemName).Parent = ToolFolder - end - end - - -- Armor - local ArmorFolder = Items:FindFirstChild("Armor") - for _, ItemName in Data.Items.Armor do - local Armor = ContentLibrary.Armor[ItemName] - if Armor then - CreateStat("BoolValue", ItemName).Parent = ArmorFolder - end - end - - -- Preferences / Misc - (pData:FindFirstChild("ActiveArmor") :: StringValue).Value = Data.ActiveArmor - - local HotbarFolder = pData:FindFirstChild("Hotbar") - for SlotNumber, ItemName in Data.Hotbar do - (HotbarFolder:FindFirstChild(SlotNumber) :: StringValue).Value = ItemName - end -end - --- Attempt to save user data. Returns whether or not the request was successful. -local function SaveData(Player: Player): boolean - if not Player:GetAttribute("DataLoaded") then - return false - end - - local pData = PlayerData:FindFirstChild(Player.UserId) - local StarterGear = Player:FindFirstChild("StarterGear") - if not pData or not StarterGear then - return false - end - - -- Same Key Cooldown (can't write to the same key within 6 seconds) - if SameKeyCooldown[Player.UserId] then - repeat task.wait() until not SameKeyCooldown[Player.UserId] - end - SameKeyCooldown[Player.UserId] = true - task.delay(6, function() - SameKeyCooldown[Player.UserId] = nil - end) - - -- Compile "DataToSave" table, which we pass to GlobalDataStore:SetAsync -- - local DataToSave = {} - - DataToSave.Stats = {} - DataToSave.Items = { - Tool = {}, - Armor = {} - } - - -- Stats - local Stats = pData:FindFirstChild("Stats") - for _, ValueObject in Stats:GetChildren() do - DataToSave.Stats[ValueObject.Name] = ValueObject.Value - end - - local Items = pData:FindFirstChild("Items") - - -- Tool - local Tool = Items:FindFirstChild("Tool") - for _, ValueObject in Tool:GetChildren() do - if ContentLibrary.Tool[ValueObject.Name] then - table.insert(DataToSave.Items.Tool, ValueObject.Name) - end - end - - -- Armor - local Armor = Items:FindFirstChild("Armor") - for _, ValueObject in Armor:GetChildren() do - if ContentLibrary.Armor[ValueObject.Name] then - table.insert(DataToSave.Items.Armor, ValueObject.Name) - end - end - - -- Preferences / Misc - DataToSave.ActiveArmor = pData.ActiveArmor.Value - - DataToSave.Hotbar = {} - for _, ValueObject in pData.Hotbar:GetChildren() do - DataToSave.Hotbar[ValueObject.Name] = ValueObject.Value - end - - -- Save to DataStore -- - local Success - for i = 1, 3 do - Success = xpcall(function() - return UserData:SetAsync("user/".. Player.UserId, DataToSave, {Player.UserId}) - end, warn) - - if Success then - break - end - task.wait(6) - end - - if Success then - print(("DataManager: User %s's data saved successfully."):format(Player.Name)) - else - warn(("DataManager: User %s's data failed to save."):format(Player.Name)) - end - - return Success -end - --- Attempt to load user data. Returns whether or not the request was successful, as well as the data if it was. -local function LoadData(Player: Player): (boolean, any) - local Success, Response = xpcall(function() - return UserData:GetAsync("user/".. Player.UserId) - end, warn) - - if Success and Response then - print(("DataManager: User %s's data loaded into the game with Level '%s'."):format(Player.Name, Response.Stats.Level)) - else - print(("DataManager: User %s had no data to load from."):format(Player.Name)) - end - - return Success, Response -end - -local function OnPlayerAdded(Player: Player) - local Success, Data = LoadData(Player) - - if not Success then - CollectionService:AddTag(Player, "DataFailed") - Player:Kick("Data unable to load. DataStore Service may be down. Please rejoin later.") - return - end - - local pData = CreateDataFolder(Player) - - if Data then - UnloadData(Player, Data, pData) - end - - pData.Parent = PlayerData - - Player:SetAttribute("DataLoaded", true) -end - -Players.PlayerAdded:Connect(OnPlayerAdded) -for _, Player in Players:GetPlayers() do - OnPlayerAdded(Player) -end - --- Save on leave -Players.PlayerRemoving:Connect(function(Player) - SaveData(Player) -end) - --- Server closing (save) -game:BindToClose(function() - task.wait(RunService:IsStudio() and 1 or 10) -end) - --- Auto-save -while true do - task.wait(60) - for _, Player in Players:GetPlayers() do - task.defer(SaveData, Player) - end -end \ No newline at end of file diff --git a/src/Server/Proxy/PlayerInfoProxy.luau b/src/Server/Proxy/PlayerInfoProxy.luau deleted file mode 100644 index 770a733..0000000 --- a/src/Server/Proxy/PlayerInfoProxy.luau +++ /dev/null @@ -1,25 +0,0 @@ --- 玩家基础信息代理 -local PlayerInfoProxy = {} - ---> Services -local ReplicatedStorage = game:GetService("ReplicatedStorage") - ---> Variables - ---> Constants -local STORE_NAME = "PlayerInfo" --------------------------------------------------------------------------------- - -function PlayerInfoProxy:InitPlayer(Player: Player) - -end - - -function PlayerInfoProxy:OnPlayerRemoving(Player: Player) - -end - - -ReplicatedStorage.Remotes.PlayerRemoving:Connect(PlayerInfoProxy.OnPlayerRemoving) - -return PlayerInfoProxy \ No newline at end of file diff --git a/src/Server/ServerMain/init.server.luau b/src/Server/ServerMain/init.server.luau index f0d9d68..1923ac7 100644 --- a/src/Server/ServerMain/init.server.luau +++ b/src/Server/ServerMain/init.server.luau @@ -12,17 +12,38 @@ local ReplicatedStorage = game:GetService("ReplicatedStorage") local ServerStorage = game:GetService("ServerStorage") local Players = game:GetService("Players") +-- 加载Proxy目录下的所有代理 +local Proxies = {} +local ProxyFolder = ServerStorage:FindFirstChild("Proxy") +if ProxyFolder then + for _, proxyModule in ipairs(ProxyFolder:GetChildren()) do + if proxyModule:IsA("ModuleScript") then + local success, result = pcall(require, proxyModule) + if success then + -- 去掉文件名后缀 + local name = proxyModule.Name + Proxies[name] = result + else + warn("加载代理模块失败: " .. proxyModule.Name, result) + end + end + end +end + --> References -local PlayerData = ReplicatedStorage:WaitForChild("PlayerData") +-- 由ArchiveProxy生成 +local PlayerDataFolder = ReplicatedStorage:WaitForChild("PlayerData") --> Dependencies -local HumanoidAttributes = require(script.HumanoidAttributes) -local PlayerLeveling = require(script.PlayerLeveling) +-- local HumanoidAttributes = require(script.HumanoidAttributes) +-- local PlayerLeveling = require(script.PlayerLeveling) -local GameConfig = require(ReplicatedStorage.Data.GameConfig) -local ContentLibrary = require(ReplicatedStorage.Modules.ContentLibrary) -local ToolLib = require(ServerStorage.Modules.ToolLib) -local ArmorLib = require(ServerStorage.Modules.ArmorLib) +-- local GameConfig = require(ReplicatedStorage.Data.GameConfig) +-- local ContentLibrary = require(ReplicatedStorage.Modules.ContentLibrary) +-- local ToolLib = require(ServerStorage.Modules.ToolLib) +-- local ArmorLib = require(ServerStorage.Modules.ArmorLib) + +-- local ArchiveProxy = require(ServerStorage.Proxy.ArchiveProxy) -------------------------------------------------------------------------------- @@ -39,29 +60,6 @@ local Temporary = CreateFolder("Temporary", workspace) local ProjectileCache = CreateFolder("ProjectileCache", Temporary) local Characters = CreateFolder("Characters", workspace) --- 初始化玩家信息存储目录(沟通作用,具体数据还得后端处理) -local PlayerDataFolder = Instance.new("Configuration") -PlayerDataFolder.Name = "PlayerData" -PlayerDataFolder.Parent = ReplicatedStorage - --- 加载Proxy目录下的所有代理 -local Proxies = {} -local ProxyFolder = script.Parent.Parent:FindFirstChild("Proxy") -if ProxyFolder then - for _, proxyModule in ipairs(ProxyFolder:GetChildren()) do - if proxyModule:IsA("ModuleScript") then - local success, result = pcall(require, proxyModule) - if success then - -- 去掉文件名后缀 - local name = proxyModule.Name - Proxies[name] = result - else - warn("加载代理模块失败: " .. proxyModule.Name, result) - end - end - end -end - -- Initially require all server-sided & shared modules -- 加载模块 ReplicatedStorage.Modules, ServerStorage.Modules for _, Location in {ReplicatedStorage.Modules, ServerStorage.Modules} do @@ -79,16 +77,26 @@ local function OnPlayerAdded(Player: Player) warn("玩家数据未加载: " .. Player.Name) return end + -- 玩家数据加载成功 + print("玩家数据加载成功: ", Proxies.ArchiveProxy.pData[Player.UserId]) local pData = Instance.new("Configuration") pData.Name = Player.UserId pData.Parent = PlayerDataFolder + + -- 初始化玩家数据目录(其他具体内容在Proxy中处理) + if not Proxies.ArchiveProxy.pData[Player.UserId] then + Proxies.ArchiveProxy.pData[Player.UserId] = {} + end + -- 加载对应玩家的其他系统代理 Proxies.EquipmentProxy:InitPlayer(Player) Proxies.PlayerInfoProxy:InitPlayer(Player) + Proxies.LevelProxy:InitPlayer(Player) end local function OnPlayerRemoving(Player: Player) + -- 清理目录(整体数据在ArchiveProxy中清除) local pData = PlayerDataFolder:FindFirstChild(Player.UserId) if pData then pData:Destroy() end end diff --git a/src/ServerStorage/Modules/ArmorLib/init.luau b/src/ServerStorage/Modules/ArmorLib/init.luau index 535e140..b082db7 100644 --- a/src/ServerStorage/Modules/ArmorLib/init.luau +++ b/src/ServerStorage/Modules/ArmorLib/init.luau @@ -1,165 +1,165 @@ ---[[ - Evercyan @ March 2023 - ArmorLib +-- --[[ +-- Evercyan @ March 2023 +-- ArmorLib - ArmorLib is an item library that houses code that can be ran on the server relating - to Armor, such as ArmorLib:Give(Player, Armor (ContentLib.Armor[...])), as well - as equipping & unequipping armor, which is primarily ran through remotes fired from the - client's Inventory Gui. -]] +-- ArmorLib is an item library that houses code that can be ran on the server relating +-- to Armor, such as ArmorLib:Give(Player, Armor (ContentLib.Armor[...])), as well +-- as equipping & unequipping armor, which is primarily ran through remotes fired from the +-- client's Inventory Gui. +-- ]] ---> Services -local ReplicatedStorage = game:GetService("ReplicatedStorage") -local Players = game:GetService("Players") +-- --> Services +-- local ReplicatedStorage = game:GetService("ReplicatedStorage") +-- local Players = game:GetService("Players") ---> References -local PlayerData = ReplicatedStorage:WaitForChild("PlayerData") +-- --> References +-- local PlayerData = ReplicatedStorage:WaitForChild("PlayerData") ---> Dependencies -local ContentLibrary = require(ReplicatedStorage.Modules.ContentLibrary) -local Morph = require(script.Morph) +-- --> Dependencies +-- local ContentLibrary = require(ReplicatedStorage.Modules.ContentLibrary) +-- local Morph = require(script.Morph) ---> Variables +-- --> Variables local ArmorLib = {} --------------------------------------------------------------------------------- +-- -------------------------------------------------------------------------------- --- Adds the armor to the player's data -function ArmorLib:Give(Player: Player, Armor) - local pData = PlayerData:WaitForChild(Player.UserId, 5) +-- -- Adds the armor to the player's data +-- function ArmorLib:Give(Player: Player, Armor) +-- local pData = PlayerData:WaitForChild(Player.UserId, 5) - if pData then - if not pData.Items.Armor:FindFirstChild(Armor.Name) then - local ValueObject = Instance.new("BoolValue") - ValueObject.Name = Armor.Name - ValueObject.Parent = pData.Items.Armor - end - else - warn(("pData for Player '%s' doesn't exist! Did they leave?"):format(Player.Name)) - end -end +-- if pData then +-- if not pData.Items.Armor:FindFirstChild(Armor.Name) then +-- local ValueObject = Instance.new("BoolValue") +-- ValueObject.Name = Armor.Name +-- ValueObject.Parent = pData.Items.Armor +-- end +-- else +-- warn(("pData for Player '%s' doesn't exist! Did they leave?"):format(Player.Name)) +-- end +-- end -function ArmorLib:Trash(Player: Player, Armor) - local pData = PlayerData:WaitForChild(Player.UserId, 5) +-- function ArmorLib:Trash(Player: Player, Armor) +-- local pData = PlayerData:WaitForChild(Player.UserId, 5) - if pData then - if pData.Items.Armor:FindFirstChild(Armor.Name) then - pData.Items.Armor[Armor.Name]:Destroy() - end - if pData.ActiveArmor.Value == Armor.Name then - pData.ActiveArmor.Value = "" - ArmorLib:UnequipArmor(Player) - end - else - warn(("pData for Player '%s' doesn't exist! Did they leave?"):format(Player.Name)) - end -end +-- if pData then +-- if pData.Items.Armor:FindFirstChild(Armor.Name) then +-- pData.Items.Armor[Armor.Name]:Destroy() +-- end +-- if pData.ActiveArmor.Value == Armor.Name then +-- pData.ActiveArmor.Value = "" +-- ArmorLib:UnequipArmor(Player) +-- end +-- else +-- warn(("pData for Player '%s' doesn't exist! Did they leave?"):format(Player.Name)) +-- end +-- end -function ArmorLib:EquipArmor(Player: Player, Armor) - local Character = Player.Character - local Humanoid = Character and Character:FindFirstChild("Humanoid") - local Attributes = Humanoid and Humanoid:WaitForChild("Attributes", 1) - if not Attributes or Humanoid.Health <= 0 then return end +-- function ArmorLib:EquipArmor(Player: Player, Armor) +-- local Character = Player.Character +-- local Humanoid = Character and Character:FindFirstChild("Humanoid") +-- local Attributes = Humanoid and Humanoid:WaitForChild("Attributes", 1) +-- if not Attributes or Humanoid.Health <= 0 then return end - -- Humanoid changes - Attributes.Health:SetAttribute("Armor", Armor.Config.Health) - Attributes.WalkSpeed:SetAttribute("Armor", Armor.Config.WalkSpeed) - Attributes.JumpPower:SetAttribute("Armor", Armor.Config.JumpPower) +-- -- Humanoid changes +-- Attributes.Health:SetAttribute("Armor", Armor.Config.Health) +-- Attributes.WalkSpeed:SetAttribute("Armor", Armor.Config.WalkSpeed) +-- Attributes.JumpPower:SetAttribute("Armor", Armor.Config.JumpPower) - -- Morph changes - Morph:ApplyOutfit(Player, Armor) -end +-- -- Morph changes +-- Morph:ApplyOutfit(Player, Armor) +-- end -function ArmorLib:UnequipArmor(Player: Player) - local Character = Player.Character - local Humanoid = Character and Character:FindFirstChild("Humanoid") - local Attributes = Humanoid and Humanoid:FindFirstChild("Attributes") - if not Attributes then return end +-- function ArmorLib:UnequipArmor(Player: Player) +-- local Character = Player.Character +-- local Humanoid = Character and Character:FindFirstChild("Humanoid") +-- local Attributes = Humanoid and Humanoid:FindFirstChild("Attributes") +-- if not Attributes then return end - -- Humanoid changes - Attributes.Health:SetAttribute("Armor", nil) - Attributes.WalkSpeed:SetAttribute("Armor", nil) - Attributes.JumpPower:SetAttribute("Armor", nil) +-- -- Humanoid changes +-- Attributes.Health:SetAttribute("Armor", nil) +-- Attributes.WalkSpeed:SetAttribute("Armor", nil) +-- Attributes.JumpPower:SetAttribute("Armor", nil) - -- Morph changes - Morph:ClearOutfit(Player) -end +-- -- Morph changes +-- Morph:ClearOutfit(Player) +-- end ----- Remotes ------------------------------------------------------------------- +-- ---- Remotes ------------------------------------------------------------------- -local ChangeCd = {} +-- local ChangeCd = {} -ReplicatedStorage.Remotes.EquipArmor.OnServerInvoke = function(Player, ArmorName: string) - if not ArmorName or typeof(ArmorName) ~= "string" then - return - end +-- ReplicatedStorage.Remotes.EquipArmor.OnServerInvoke = function(Player, ArmorName: string) +-- if not ArmorName or typeof(ArmorName) ~= "string" then +-- return +-- end - local Armor = ContentLibrary.Armor[ArmorName] +-- local Armor = ContentLibrary.Armor[ArmorName] - if Armor and not ChangeCd[Player.UserId] then - ChangeCd[Player.UserId] = true - task.delay(0.25, function() - ChangeCd[Player.UserId] = nil - end) +-- if Armor and not ChangeCd[Player.UserId] then +-- ChangeCd[Player.UserId] = true +-- task.delay(0.25, function() +-- ChangeCd[Player.UserId] = nil +-- end) - ArmorLib:EquipArmor(Player, Armor) +-- ArmorLib:EquipArmor(Player, Armor) - local pData = PlayerData:FindFirstChild(Player.UserId) - if pData and pData.Items.Armor[ArmorName] and Armor then - pData.ActiveArmor.Value = ArmorName - end - end -end +-- local pData = PlayerData:FindFirstChild(Player.UserId) +-- if pData and pData.Items.Armor[ArmorName] and Armor then +-- pData.ActiveArmor.Value = ArmorName +-- end +-- end +-- end -ReplicatedStorage.Remotes.UnequipArmor.OnServerInvoke = function(Player) - ArmorLib:UnequipArmor(Player) +-- ReplicatedStorage.Remotes.UnequipArmor.OnServerInvoke = function(Player) +-- ArmorLib:UnequipArmor(Player) - local pData = PlayerData:FindFirstChild(Player.UserId) - if pData then - pData.ActiveArmor.Value = "" - end -end +-- local pData = PlayerData:FindFirstChild(Player.UserId) +-- if pData then +-- pData.ActiveArmor.Value = "" +-- end +-- end --------------------------------------------------------------------------------- +-- -------------------------------------------------------------------------------- -local function OnPlayerAdded(Player: Player) - local pData = PlayerData:WaitForChild(Player.UserId) +-- local function OnPlayerAdded(Player: Player) +-- local pData = PlayerData:WaitForChild(Player.UserId) - local function OnCharacterAdded(Character) - local ActiveArmor = pData:WaitForChild("ActiveArmor") - local Armor = ActiveArmor.Value ~= "" and ContentLibrary.Armor[ActiveArmor.Value] +-- local function OnCharacterAdded(Character) +-- local ActiveArmor = pData:WaitForChild("ActiveArmor") +-- local Armor = ActiveArmor.Value ~= "" and ContentLibrary.Armor[ActiveArmor.Value] - -- Update any incoming accessories (CharacterAppearanceLoaded is really broken lol) - local Connection = Character.ChildAdded:Connect(function(Child) - if Child:IsA("Accessory") then - Morph:UpdateAccessoriesTransparency(Character, ActiveArmor.Value ~= "" and ContentLibrary.Armor[ActiveArmor.Value]) - end - end) - Player.CharacterRemoving:Once(function() - Connection:Disconnect() - end) +-- -- Update any incoming accessories (CharacterAppearanceLoaded is really broken lol) +-- local Connection = Character.ChildAdded:Connect(function(Child) +-- if Child:IsA("Accessory") then +-- Morph:UpdateAccessoriesTransparency(Character, ActiveArmor.Value ~= "" and ContentLibrary.Armor[ActiveArmor.Value]) +-- end +-- end) +-- Player.CharacterRemoving:Once(function() +-- Connection:Disconnect() +-- end) - if Armor then - if Player:HasAppearanceLoaded() then - ArmorLib:EquipArmor(Player, Armor) - else - Player.CharacterAppearanceLoaded:Once(function() - ArmorLib:EquipArmor(Player, Armor) - end) - end - end - end +-- if Armor then +-- if Player:HasAppearanceLoaded() then +-- ArmorLib:EquipArmor(Player, Armor) +-- else +-- Player.CharacterAppearanceLoaded:Once(function() +-- ArmorLib:EquipArmor(Player, Armor) +-- end) +-- end +-- end +-- end - Player.CharacterAdded:Connect(OnCharacterAdded) - if Player.Character then - OnCharacterAdded(Player.Character) - end -end +-- Player.CharacterAdded:Connect(OnCharacterAdded) +-- if Player.Character then +-- OnCharacterAdded(Player.Character) +-- end +-- end -for _, Player in Players:GetChildren() do - task.defer(OnPlayerAdded, Player) -end +-- for _, Player in Players:GetChildren() do +-- task.defer(OnPlayerAdded, Player) +-- end -Players.PlayerAdded:Connect(OnPlayerAdded) +-- Players.PlayerAdded:Connect(OnPlayerAdded) return ArmorLib \ No newline at end of file diff --git a/src/Server/Proxy/ArchiveProxy.luau b/src/ServerStorage/Proxy/ArchiveProxy.luau similarity index 81% rename from src/Server/Proxy/ArchiveProxy.luau rename to src/ServerStorage/Proxy/ArchiveProxy.luau index c88ea6d..7ca0269 100644 --- a/src/Server/Proxy/ArchiveProxy.luau +++ b/src/ServerStorage/Proxy/ArchiveProxy.luau @@ -1,6 +1,8 @@ -- 数据存储代理 local ArchiveProxy = {} +print("进入") + --> Services local CollectionService = game:GetService("CollectionService") local ReplicatedStorage = game:GetService("ReplicatedStorage") @@ -14,7 +16,7 @@ local GameConfig = require(ReplicatedStorage.Data.GameConfig) local ContentLibrary = require(ReplicatedStorage.Modules.ContentLibrary) --> Variables -local UserData = DataStoreService:GetDataStore("UserData") +local UserData = DataStoreService:GetDataStore("UserData3") local SameKeyCooldown = {} -------------------------------------------------------------------------------- @@ -47,24 +49,8 @@ local function SaveData(Player: Player): boolean if not Player:GetAttribute("DataLoaded") then return false end - - local pData = PlayerData:FindFirstChild(Player.UserId) - local StarterGear = Player:FindFirstChild("StarterGear") - if not pData or not StarterGear then - return false - end - - -- Same Key Cooldown (can't write to the same key within 6 seconds) - if SameKeyCooldown[Player.UserId] then - repeat task.wait() until not SameKeyCooldown[Player.UserId] - end - SameKeyCooldown[Player.UserId] = true - task.delay(6, function() - SameKeyCooldown[Player.UserId] = nil - end) - - -- Compile "DataToSave" table, which we pass to GlobalDataStore:SetAsync -- - local DataToSave = {} + + local DataToSave = ArchiveProxy.pData[Player.UserId] -- Save to DataStore -- local Success @@ -95,7 +81,8 @@ local function LoadData(Player: Player): (boolean, any) end, warn) if Success and Response then - print(("DataManager: User %s's data loaded into the game with Level '%s'."):format(Player.Name, Response.Stats.Level)) + print(("DataManager: User %s's data loaded into the game."):format(Player.Name)) + print(Response) else print(("DataManager: User %s had no data to load from."):format(Player.Name)) end @@ -114,12 +101,17 @@ local function OnPlayerAdded(Player: Player) if not ArchiveProxy.pData then ArchiveProxy.pData = {} end + + -- 如果数据为空,则初始化数据 + if not Data then + Data = {} + end ArchiveProxy.pData[Player.UserId] = Data Player:SetAttribute("DataLoaded", true) end -local function OnPlayerRemoving(Player: Player) +local function OnPlayerRemoving(Player: string) SaveData(Player) ArchiveProxy.pData[Player.UserId] = nil ReplicatedStorage.Remotes.PlayerRemoving:Fire(Player) @@ -139,7 +131,6 @@ game:BindToClose(function() task.wait(RunService:IsStudio() and 1 or 10) end) - -- Auto-save task.spawn(function() while true do diff --git a/src/Server/Proxy/EquipmentProxy.luau b/src/ServerStorage/Proxy/EquipmentProxy.luau similarity index 73% rename from src/Server/Proxy/EquipmentProxy.luau rename to src/ServerStorage/Proxy/EquipmentProxy.luau index ff3b050..42a3b62 100644 --- a/src/Server/Proxy/EquipmentProxy.luau +++ b/src/ServerStorage/Proxy/EquipmentProxy.luau @@ -3,18 +3,22 @@ local EquipmentProxy = {} --> Services local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") --> Variables local Utils = require(ReplicatedStorage.Tools.Utils) -local EquipmentJsonData = require(ReplicatedStorage.Json.Equipment) -local ArchiveProxy = require(ReplicatedStorage.Modules.ArchiveProxy) -local PlayerInfoProxy = require(ReplicatedStorage.Modules.PlayerInfoProxy) +local ArchiveProxy = require(ServerStorage.Proxy.ArchiveProxy) +local PlayerInfoProxy = require(ServerStorage.Proxy.PlayerInfoProxy) + +--> Json +local JsonEquipment = require(ReplicatedStorage.Json.Equipment) --> Constants local STORE_NAME = "Equipment" -------------------------------------------------------------------------------- +-- 获取装备文件夹 local function GetPlayerEquipmentFolder(Player: Player) local pData = Utils:GetPlayerDataFolder(Player) if not pData then return end @@ -22,10 +26,10 @@ local function GetPlayerEquipmentFolder(Player: Player) return EquipmentFolder end - +-- 创建装备实例 local function CreateEquipmentInstance(Player: Player, UniqueId: number, EquipmentData: table) if Player or UniqueId or EquipmentData then - warn('创建装备实例失败: ' .. Player.Name .. ' ' .. UniqueId .. ' ' .. EquipmentData) + warn('创建装备实例失败: ' , Player.Name, UniqueId, EquipmentData) return end local PlayerEquipmentFolder = GetPlayerEquipmentFolder(Player) @@ -40,29 +44,30 @@ end -------------------------------------------------------------------------------- +-- 初始化玩家 function EquipmentProxy:InitPlayer(Player: Player) local pData = Utils:GetPlayerDataFolder(Player) if not pData then return end - local EquipmentFolder = Utils:CreateFolder("Equipment", pData) + Utils:CreateFolder("Equipment", pData) - -- 初始化数据存储 - if not ArchiveProxy.pData[Player.UserId] then - ArchiveProxy.pData[Player.UserId] = {} + -- 新玩家数据初始化 + if not ArchiveProxy.pData[Player.UserId][STORE_NAME] then + ArchiveProxy.pData[Player.UserId][STORE_NAME] = {} end -- 初始化装备 - for uniqueId, EquipmentData in ArchiveProxy.pData[Player.UserId] do + for uniqueId, EquipmentData in ArchiveProxy.pData[Player.UserId][STORE_NAME] do CreateEquipmentInstance(Player, uniqueId, EquipmentData) end end -local EXCEPT_KEYS = { "id", "orgId", "name"} +local EXCEPT_KEYS = { "id", "orgId", "name", "attributes"} -- 添加装备到背包 function EquipmentProxy:AddEquipment(Player: Player, EquipmentId: number) local pData = Utils:GetPlayerDataFolder(Player) if not pData then return end - local EquipmentData = Utils:GetJsonIdData("Equipment", EquipmentId) + local EquipmentData = Utils:GetIdDataFromJson(JsonEquipment, EquipmentId) if not EquipmentData then return end local UniqueId = Utils:GenUniqueId(ArchiveProxy.pData[Player.UserId]) @@ -79,7 +84,7 @@ function EquipmentProxy:AddEquipment(Player: Player, EquipmentId: number) ResultData.wearing = false -- 其他随机词条内容添加在下面 - -- 根据词条内容直接生成回收奖励 + -- 之后回收修改随机生成 ------------------------------------------------------------ @@ -87,17 +92,16 @@ function EquipmentProxy:AddEquipment(Player: Player, EquipmentId: number) CreateEquipmentInstance(Player, UniqueId, ResultData) end +-- 回收装备 function EquipmentProxy:RecycleEquipment(Player: Player, EquipmentId: number) local pData = Utils:GetPlayerDataFolder(Player) if not pData then return end - local EquipmentData = Utils:GetJsonIdData("Equipment", EquipmentId) + local EquipmentData = ArchiveProxy.pData[Player.UserId][STORE_NAME][EquipmentId] if not EquipmentData then return end - - if not ArchiveProxy.pData[Player.UserId][EquipmentId] then return end -- 回收装备返回金币 - -- 调用PlayerInfoProxy来增加货币 + PlayerInfoProxy:ChangeItem(Player, 1, EquipmentData.recycle) ArchiveProxy.pData[Player.UserId][EquipmentId] = nil local EquipmentInstance = GetPlayerEquipmentFolder(Player):FindFirstChild(EquipmentId) @@ -106,6 +110,7 @@ function EquipmentProxy:RecycleEquipment(Player: Player, EquipmentId: number) end end +-- 穿戴装备 function EquipmentProxy:WearEquipment(Player: Player, EquipmentId: number) local pData = Utils:GetPlayerDataFolder(Player) if not pData then return end @@ -119,6 +124,8 @@ function EquipmentProxy:OnPlayerRemoving(Player: Player) end -ReplicatedStorage.Remotes.PlayerRemoving:Connect(EquipmentProxy.OnPlayerRemoving) +ReplicatedStorage.Remotes.PlayerRemoving.Event:Connect(function(PlayerUserId: string) + EquipmentProxy:OnPlayerRemoving(PlayerUserId) +end) return EquipmentProxy \ No newline at end of file diff --git a/src/ServerStorage/Proxy/ItemProxy.luau b/src/ServerStorage/Proxy/ItemProxy.luau new file mode 100644 index 0000000..04678c0 --- /dev/null +++ b/src/ServerStorage/Proxy/ItemProxy.luau @@ -0,0 +1,31 @@ +-- 道具代理 +local ItemProxy = {} + +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Json +local JsonItem = require(ReplicatedStorage.Json.ItemProp) + +--> Variables +local Utils = require(ReplicatedStorage.Tools.Utils) +-- local ArchiveProxy = require(ReplicatedStorage.Modules.ArchiveProxy) +local PlayerInfoProxy = require(ServerStorage.Proxy.PlayerInfoProxy) + +-------------------------------------------------------------------------------- + +function ItemProxy:AddItem(Player: Player, ItemId: number, ItemCount: number) + local pData = Utils:GetPlayerDataFolder(Player) + if not pData then return end + + local ItemData = Utils:GetIdDataFromJson(JsonItem, ItemId) + if not ItemData then return end + + -- 之后根据不同类型做处理 + if ItemData.type == 1 then + PlayerInfoProxy:ChangeItemCount(Player, ItemId, ItemCount) + end +end + +return ItemProxy \ No newline at end of file diff --git a/src/ServerStorage/Proxy/LevelProxy.luau b/src/ServerStorage/Proxy/LevelProxy.luau new file mode 100644 index 0000000..5f14643 --- /dev/null +++ b/src/ServerStorage/Proxy/LevelProxy.luau @@ -0,0 +1,125 @@ +-- 关卡代理 +local LevelProxy = {} + +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") +local Players = game:GetService("Players") + +--> Variables +local Utils = require(ReplicatedStorage.Tools.Utils) +local ArchiveProxy = require(ServerStorage.Proxy.ArchiveProxy) + +--> Json +local JsonLevel = require(ReplicatedStorage.Json.Level) + +--> Constants +local STORE_NAME = "Level" +local ENUM_LEVEL_TYPE = { + Main = 1, +} + +-------------------------------------------------------------------------------- + +-- 初始化生成关卡目录 +local LevelFolder = Utils:CreateFolder(STORE_NAME, game.Workspace) + +-------------------------------------------------------------------------------- + +-- 获取玩家关卡文件夹 +local function GetPlayerLevelFolder(Player: Player) + local pData = Utils:GetPlayerDataFolder(Player) + if not pData then return end + local LevelFolder = pData:FindFirstChild("Level") + return LevelFolder +end + +-- 获取玩家关卡Workspace目录 +local function GetPlayerLevelWorkspaceFolder(PlayerUserId: string) + return LevelFolder:FindFirstChild(PlayerUserId) +end + +-- 创建关卡信息实例 +local function CreateLevelInstance(Player: Player, Folder: Instance, LevelKey: string, LevelValue: number) + if not Player or not Folder or not LevelKey or not LevelValue then return end + local LevelInstance = Instance.new("NumberValue") + LevelInstance.Name = LevelKey + LevelInstance.Parent = Folder + LevelInstance.Value = LevelValue + return LevelInstance +end + +-- 初始化玩家关卡信息 +local function ExtraAddPlayerLevel(Player: Player, LevelData: table) + if not Player or not LevelData then return end + -- 如果列表中不包含信息就添加到表中 + for LevelKey, LevelValue in ENUM_LEVEL_TYPE do + if not LevelData[LevelKey] then + LevelData[LevelKey] = LevelValue + end + end +end + +-------------------------------------------------------------------------------- + +function LevelProxy:InitPlayer(Player: Player) + local pData = Utils:GetPlayerDataFolder(Player) + if not pData then return end + local LevelFolder = Utils:CreateFolder(STORE_NAME, pData) + local ProgressFolder = Utils:CreateFolder("Progress", LevelFolder) + local DungeonFolder = Utils:CreateFolder("Dungeon", LevelFolder) + -- 当前关卡状态 + Utils:CreateFolder("Stats", LevelFolder) + + -- 关卡目录下生成玩家目录 + local spawnFloder = Utils:CreateFolder(Player.UserId, game.Workspace:FindFirstChild(STORE_NAME)) + print("spawnFloder: ", spawnFloder.Name) + + -- 新玩家数据初始化 + if not ArchiveProxy.pData[Player.UserId][STORE_NAME] then + ArchiveProxy.pData[Player.UserId][STORE_NAME] = {} + ArchiveProxy.pData[Player.UserId][STORE_NAME].Progress = {} + -- 副本之后再做 + ArchiveProxy.pData[Player.UserId][STORE_NAME].Dungeon = {} + end + + ExtraAddPlayerLevel(Player, ArchiveProxy.pData[Player.UserId][STORE_NAME].Progress) + + -- 前端变化 + for LevelKey, LevelValue in ArchiveProxy.pData[Player.UserId][STORE_NAME].Progress do + CreateLevelInstance(Player, ProgressFolder, LevelKey, LevelValue) + end +end + +-- 挑战关卡(挑战副本用另一个函数) +function LevelProxy:ChallengeLevel(Player: Player, LevelId: number) + -- 给前端传数据,做表现 + + -- 场景后端生成 + + -- 后端生成当前关卡状态数据 +end + +-- 挑战结束 +function LevelProxy:ChallengeEnd(Player: Player) + -- 判断玩家是否通关 + -- 通关后,没到最大关卡,关卡进度+1 + -- 到达最大关卡不做处理 +end + +function LevelProxy:OnPlayerRemoving(Player: Player) + local PlayerLevelFolder = GetPlayerLevelWorkspaceFolder(Player.UserId) + if PlayerLevelFolder then + PlayerLevelFolder:Destroy() + end +end + +-- ReplicatedStorage.Remotes.PlayerRemoving.Event:Connect(function(PlayerUserId: string) +-- LevelProxy:OnPlayerRemoving(PlayerUserId) +-- end) + +Players.PlayerRemoving:Connect(function(Player: Player) + LevelProxy:OnPlayerRemoving(Player) +end) + +return LevelProxy \ No newline at end of file diff --git a/src/ServerStorage/Proxy/MobAIProxy.luau b/src/ServerStorage/Proxy/MobAIProxy.luau new file mode 100644 index 0000000..282ec5e --- /dev/null +++ b/src/ServerStorage/Proxy/MobAIProxy.luau @@ -0,0 +1,4 @@ +-- 怪物AI代理 +local MobAIProxy = {} + +return MobAIProxy \ No newline at end of file diff --git a/src/ServerStorage/Proxy/PlayerInfoProxy.luau b/src/ServerStorage/Proxy/PlayerInfoProxy.luau new file mode 100644 index 0000000..b949cf0 --- /dev/null +++ b/src/ServerStorage/Proxy/PlayerInfoProxy.luau @@ -0,0 +1,172 @@ +-- 玩家基础信息代理 +local PlayerInfoProxy = {} + +--> Services +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ServerStorage = game:GetService("ServerStorage") + +--> Variables +local Utils = require(ReplicatedStorage.Tools.Utils) +local ArchiveProxy = require(ServerStorage.Proxy.ArchiveProxy) + +--> Json +local JsonPlayerLv = require(ReplicatedStorage.Json.PlayerLv) +local JsonItem = require(ReplicatedStorage.Json.ItemProp) + +--> Constants +local STORE_NAME = "PlayerInfo" +local ENUM_STATE_TYPE = { + Number = "NumberValue", + String = "StringValue", + Bool = "BoolValue", + Vector3 = "Vector3Value", + Vector2 = "Vector2Value", +} + +-------------------------------------------------------------------------------- + +-- 获取玩家信息文件夹 +local function GetPlayerInfoFolder(Player: Player) + local pData = Utils:GetPlayerDataFolder(Player) + if not pData then return end + local PlayerInfoFolder = pData:FindFirstChild("PlayerInfo") + return PlayerInfoFolder +end + +-- 创建玩家信息实例 +local function CreateInfoInstance(Player: Player, Folder: any, StateKey: string, StateType: string, StateValue: any) + if not Player and not Folder and not StateKey and not StateType then + warn('创建玩家信息实例失败: ' , Player.Name, Folder.Name, StateKey, StateType, StateValue) + return + end + + local Info = Instance.new(StateType) + Info.Name = StateKey + Info.Parent = Folder + if StateValue then Info.Value = StateValue end + return Info +end + +-- 改变玩家实例信息 +local function ChangeInfoInstance(Player: Player, Folder: any, StateKey: string, StateValue: any) + if not Folder or not StateKey or not StateValue then return end + local Info = Folder:FindFirstChild(StateKey) + if not Info then warn('玩家信息实例不存在: ' , Player.Name, StateKey) return end + Info.Value = StateValue +end + +local STATS_DATA = { + name = {type = ENUM_STATE_TYPE.String, value = "PlayerName"}, + level = {type = ENUM_STATE_TYPE.Number, value = 1}, + exp = {type = ENUM_STATE_TYPE.Number, value = 0}, +} + +-- 初始化玩家状态信息 +local function ExtraAddPlayerStats(Player: Player, StatsData: table) + if not Player or not StatsData then return end + -- 如果列表中不包含信息就添加到表中 + for StateKey, StateValue in STATS_DATA do + if not StatsData[StateKey] then + StatsData[StateKey] = StateValue + end + end +end + +-------------------------------------------------------------------------------- + +-- 初始化玩家 +function PlayerInfoProxy:InitPlayer(Player: Player) + local pData = Utils:GetPlayerDataFolder(Player) + if not pData then return end + local PlayerInfoFolder = Utils:CreateFolder("PlayerInfo", pData) + local StatsFolder = Utils:CreateFolder("Stats", PlayerInfoFolder) + local ItemsFolder = Utils:CreateFolder("Items", PlayerInfoFolder) + + -- 新玩家数据初始化 + if not ArchiveProxy.pData[Player.UserId][STORE_NAME] then + ArchiveProxy.pData[Player.UserId][STORE_NAME] = {} + ArchiveProxy.pData[Player.UserId][STORE_NAME].Stats = {} + ArchiveProxy.pData[Player.UserId][STORE_NAME].Items = {} + end + + -- 放在外面是为了以后系统新增内容方便(同时不用在初始化数据是做写入了) + ExtraAddPlayerStats(Player, ArchiveProxy.pData[Player.UserId][STORE_NAME].Stats) + + -- 创建玩家信息实例 + for StateKey, StateData in ArchiveProxy.pData[Player.UserId][STORE_NAME].Stats do + CreateInfoInstance(Player, StatsFolder, StateKey, StateData.type, StateData.value) + end +end + +-- 添加经验 +function PlayerInfoProxy:AddExp(Player: Player, Exp: number) + if not Player or not Exp then warn('添加经验失败: ' .. Player.Name .. ' ' .. Exp) return end + local playerInfoData = ArchiveProxy.pData[Player.UserId][STORE_NAME] + playerInfoData.Stats.exp = playerInfoData.Stats.exp + Exp + + -- 数据变化 + local currentLevel = playerInfoData.Stats.level + local maxLevel = Utils:GetMaxIdFromJson(JsonPlayerLv) + if currentLevel < maxLevel then + local requireExp = Utils:GetIdDataFromJson(JsonPlayerLv, currentLevel) + if playerInfoData.Stats.exp >= requireExp then + playerInfoData.Stats.level = currentLevel + 1 + playerInfoData.Stats.exp = playerInfoData.Stats.exp - requireExp + end + end + + -- 前端变化 + local StatsFolder = GetPlayerInfoFolder(Player):FindFirstChild("Stats") + ChangeInfoInstance(Player, StatsFolder, "exp", playerInfoData.Stats.exp) + ChangeInfoInstance(Player, StatsFolder, "level", playerInfoData.Stats.level) + return true, playerInfoData.Stats.level, playerInfoData.Stats.exp +end + +-- 改变物品数量记录 +function PlayerInfoProxy:ChangeItemCount(Player: Player, ItemId: number, ItemCount: number) + if not Player or not ItemId or not ItemCount then warn('添加物品失败: ' , Player.Name, ItemId, ItemCount) return end + + local playerInfoData = ArchiveProxy.pData[Player.UserId][STORE_NAME].Items + local isNew = false + if not playerInfoData[ItemId] then + playerInfoData[ItemId] = ItemCount + isNew = true + else + playerInfoData[ItemId] = playerInfoData[ItemId] + ItemCount + end + + -- 前端变化 + local ItemsFolder = GetPlayerInfoFolder(Player):FindFirstChild("Items") + if isNew then + CreateInfoInstance(Player, ItemsFolder, ItemId, "NumberValue") + else + ChangeInfoInstance(Player, ItemsFolder, ItemId, playerInfoData[ItemId]) + end + return true, playerInfoData[ItemId] +end + +-- 判断是否拥有足够物品 +function PlayerInfoProxy:HasEnoughItem(Player: Player, ItemId: number, ItemCount: number) + if not Player or not ItemId or not ItemCount then warn('添加物品失败: ' .. Player.Name .. ' ' .. ItemId .. ' ' .. ItemCount) return end + local playerInfoData = ArchiveProxy.pData[Player.UserId][STORE_NAME].Items + if not playerInfoData[ItemId] then return false end + return playerInfoData[ItemId] >= ItemCount +end + +-- 获取物品数量 +function PlayerInfoProxy:GetItemCount(Player: Player, ItemId: number) + if not Player or not ItemId then warn('获取物品数量失败: ' .. Player.Name .. ' ' .. ItemId) return end + local playerInfoData = ArchiveProxy.pData[Player.UserId][STORE_NAME].Items + if not playerInfoData[ItemId] then return 0 end + return playerInfoData[ItemId] +end + +function PlayerInfoProxy:OnPlayerRemoving(Player: Player) + +end + +ReplicatedStorage.Remotes.PlayerRemoving.Event:Connect(function(Player: Player) + PlayerInfoProxy:OnPlayerRemoving(Player) +end) + +return PlayerInfoProxy \ No newline at end of file diff --git a/src/ServerStorage/Proxy/PlayerLoopProxy/PlayerAI.luau b/src/ServerStorage/Proxy/PlayerLoopProxy/PlayerAI.luau new file mode 100644 index 0000000..e69de29 diff --git a/src/ServerStorage/Proxy/PlayerLoopProxy/init.luau b/src/ServerStorage/Proxy/PlayerLoopProxy/init.luau new file mode 100644 index 0000000..a03e5cd --- /dev/null +++ b/src/ServerStorage/Proxy/PlayerLoopProxy/init.luau @@ -0,0 +1,6 @@ +-- 玩家循环代理 + +local PlayerLoopProxy = {} + + +return PlayerLoopProxy \ No newline at end of file