From 3c40c53f6627a143c71f2156ebc204575cfe56a3 Mon Sep 17 00:00:00 2001 From: NotMyFault Date: Fri, 9 Oct 2020 10:13:13 +0200 Subject: [PATCH 1/8] Add missing aliases --- .../main/java/com/sk89q/worldedit/command/RegionCommands.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index c8f5005a9..a5c184fb1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -167,6 +167,7 @@ public class RegionCommands { @Command( name = "/removelighting", + aliases = "/removelight", desc = "Removing lighting in a selection" ) @CommandPermissions("worldedit.light.remove") @@ -203,6 +204,7 @@ public class RegionCommands { @Command( name = "/setblocklight", + aliases = "/setlight", desc = "Set block lighting in a selection" ) @CommandPermissions("worldedit.light.set") From d42e152348e9afeab3c301300aa5c15660b026d8 Mon Sep 17 00:00:00 2001 From: N0tMyFaultOG Date: Fri, 9 Oct 2020 22:39:36 +0200 Subject: [PATCH 2/8] Update adapters --- .../bukkit/BukkitServerInterface.java | 3 ++- .../src/main/resources/worldedit-adapters.jar | Bin 338869 -> 339428 bytes 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index c0d778e7b..9ffee2d2f 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -89,10 +89,11 @@ public class BukkitServerInterface extends AbstractPlatform implements MultiUser return BukkitRegistries.getInstance(); } + @SuppressWarnings("deprecation") @Override public int getDataVersion() { if (plugin.getBukkitImplAdapter() != null) { - return plugin.getBukkitImplAdapter().getDataVersion(); + return Bukkit.getUnsafe().getDataVersion(); } return -1; } diff --git a/worldedit-bukkit/src/main/resources/worldedit-adapters.jar b/worldedit-bukkit/src/main/resources/worldedit-adapters.jar index 11e34adbebb9a97615cd1c01ee71fd07847acc34..d86472521c63109b64b091ae52046845c5aa3c00 100644 GIT binary patch delta 70584 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 delta 70066 zcmY(ob97zZ_dOi*#-34D};n0F!J zldxN^|B{FM32BCy#dn*>=U-7a1vj5bq53l{>-ZD-HjrKo}xig7V z<^J>d4h{^=I0^he^hNkIkpn2~MvYnZf4x-;*Z=6}EI+_W8^ z|NNi58TmI=#_#%nGblYn{c~C49Ub<6s#>j~z_l6vX2UGtR4{*+OsM3b70-WbzXvi+ zq#xo-+O7~dblT@R6f7tkXs5iOg6Vg)-5x`ZrHZMd$5PhO~03e%7zZaPCw4eF;beQ$~;q^oftdjPKEym_c z^sZNICjoF<-BCUg55ARZQZ79ykD;y5Fd#oKlZL!$U1NKJsRvZ~#IL-(eM;lEyB^CV zCWsJHnI^fbm471`I@pIT{`lhvK{Xg*qv+=XjXkx1t7N7e94rr1X=tM~pW3JUDdfbp55huKRy3h$QwE#t~H z;&s`1u-%SkT^YzwcIext8e1eH{Z(O}F&@l7lLDLG6%iiSPIT2{iKAWzI+S1DegJ?$ zPAlO?rJo)FV6zBLapiy80V&0B?i$`U<(-d#Td?60!vTFR3F9{LJIiq3=)HTvVm1_d zQFmlfFm&Y8lYijC^M^D0FiFxFpp9Ux6bCyEx#ctGHW@_rt@4^%pY`+;yXHB7N#JT& zt&~e9^Fi=Oj(C z%L|H3XPUts1+e^&Zjt^lyEwaGGQ!xTin%Pk_z#L7MB@UomlpT@rtt$%)Ct3w$E?bo zA_OYre!)s^$q@{z0kWIDHd=$S(Q2M}+LYcQ@`p5|)T8Ek+*o>24}@j1d7BJQZys#f0sVu+#&HWFxS;iUNQ zep6pW4Z!Lo-xgJ;pQ8`bIYQ*J4P`ZLAn>3mGdOJ2h)hNC^EWu`x>%D4;;CrzNuO|k zy8p7}YrW&=`3}=7tLHHnlh6E}_hMIuCboUlXjPJUB-&|kh;~@-nb+R-0GXOF%hhqb z&nh%lVz81NWPXE~KkKI$N*?}__?YPqa`Q2ITkOwv0!byC^)}O{0Y@;!(^5s*qVqputW-~1*Y%pBa$@|)h-z;ud3qfzx3m2EX6QI%*eyArnDm z0Abbxh&93mxkMmq1R6zfl&|e{51pC6ZGVrxL~wE!f56b&ohJnytEL7jKqm1zm`@R=lq>Uf{Shstku4*|NW2yD)bUM@mvdoB5 z8&`DX;;8nc92GxdQjE z@{(^2gSEIJ39({$n8iS;v7*8+^DYFfs3GdzjpG{PoBGD5Q&ZIzwwn@*)O-Nxm9&+M zhs9d#f!~wD$T&gwVP`OG!68+1jPHpJQZ}wSOV?9t4B;Mc*HWnN< zpjCCk{4NUNJBaDVdkx{sj#<$ak5R4bIWu38xCgZNWtxi?Art?%peeo8fGrU(j@9UR zp3+lnZGV9FAabi`mOEceQzsM5F#i(71CzA4_vL6yss%*Tpel`m=}hz3jf6L|6VcB^=G_{X=mn8 z|98d62!IoV9MDBEKSJg6{l(6PFpkk$v$7NjyW@WqYiO+!j>eJBUmThvm&ZDtRB zFy$)v+`v8;Mq8vJaVO)7lDJPlyqZscY5aX~(s~W%eoGrh$?Ys-L=p6gXKQxjZ?u=< zoFZf@qe7?EHWR$?DEicQCA>>!n!#2A$v=;;)^ilksQ1?CJmCkSyGvnAn zdokNhLCWoT6&xIFO%^*J4q%E*uDB93QyV=*0Dg`LOX}kuFfzM3(CJ$Fr>uwu%}AQmCm!nY`B1}_3=s4P_8uq52Dw<-z^*Au&- z8d6&P1cdPaZotZ*p7dT4)@>`Eg5c8ei7fByO&6uBDZ$T&cUb?g-_iO<22jnO1o{ua zqQG-tz7MlLXqt`Nb0ll7#NxgY5y^+HrH&tduwKfz(DK1+SThkt9LnGx-4&c}Dk>Q{9DQarS&rG`o)Im z*T}xQI;tUGFA!QIYy!~H+K3ohv2YSG+WzKyq9v6Yuv5e|Q#I*a%poKb{9dFuUMqo* z5jRs;rkDmgDfGgBpA)#iqcaYzhEJ(iXw`xFMxAXBpQB4^=0ST999{Qz$I!`w`5o>o z+gb9O<_f5X$AdbJ&ffYE*!g7~{F6ZL>t;OaHniOKk%13rvNmwi2D(S|6zllH{C9|x zxY1R?m&rIw6sdQ_bh0Yd!vpv$w9jsvN~KSj|4t0n|Nn8M`tzn{1X>UbjJhzbR|H%N zWT-xbcf<4hxJy=_7*z~{5iZa_SR_2lI8a0e0xHp;5*31;c?<$Eb1Qwz(Xy*z-*Cpa``on{_|f87+NvbA=EvCuNQh}*k+B*@@Xvgq0k(gti8W-i=)DUomXoYq zcsbG%a$&}R!XzGZ^T0wA8uk5(l-y-FryZs*zGBEo`g+3pDkoE#Uv!dA4!n5tZ3(yN zMCsxnXSM%0F;%lHb@U>&yj|pCNye;nrg^`mUHf8eNe0J!lgWIuST(I;rj_*w$huL% zu7#WC8^iLkxh2*5jcc%mwYAy0fY&5XzKX7)_0hVRnr@MXN(DP&dduKq>_X~%p^i0| zx;38QajDHjvyPf>{*vlTN=b_xzrHn%%40-Hi%wCPuEnlmWXOFy^?Avck z8%4Y9mK(;E7L2?d9^rlm*g1umekh^ z%q1gKz>Z{{?r7pP`AdZIZ&rcI9pk|<>q}?0rH_ah)60H8YQ7bIlaIT+2eri#uPhwE zcx|Tlm00nZS@PJlnp(D6x6U27mv1G2ms)$sUDg126*r8mJ#2akvb>8PZOXS8nqSKx zdlJ?iidz4^-8> z0Vm)+f=a@q3|1Ai#I6j*!q-*zs2iGBaVp4~e+avs-{*PBEP?^EYpO?||7C z@^_yI>ir|YCD!GP`V$Lv@;qFNRc*$)&H69u&XTSK=#0mf@~*-zdGS}>(8nD2sKpQF z=BMIEQJ9Ox579Uj#Y>9ITv>f(k76FWv*3j<~q(gC@uMqoV7vbtejc* z7{s^NR(S8z5f9+&cJK(Dr-=0@n^A5_sg>~TtMq5Ye43CSY7a602z{H@AbS zZ44G`V5(Muf;E*7f`Fvs8pRBfgb^y6@90k<3k}Z)5>gM8jm)H5x!V$4bfZcW0t;sm)t`1^xFK)p_uMD7-_ifv6ol|?cLlQ>Bc z-Ns7qmkK@zoqpxu5TO>6plCIGU-7xfl2e=Pj*SyeSP+7`3P%x`#VFZ0W*(I{b{rm$ z$po}z*N#}VZ2;CwMpgq2!;TS?2@wZpig*lF&Ux0d~Lm||aSU5jyLrCGtnPovZX^|82gGXf;gIGX4x*-i}{s_}a zYo}(+p7{MV>PnoI^eRz3h9P&l8jdwSr>?Q(D1xFk)yPctRcfckF`qJIDB}~&BrdY- zs11ct?A8TH$+3ZbCAzDECuFrvCO=8YZs|yah12WexowNco$#IcU5|Og>rx|!e4{J& zp=;0}BV+c!?!WPbr(Qf{9PCl($ffJxa`PW7{p_asBfVWc;wMC5`gSo}eh+QCq;t`ws@7cr-vj?>$-_<&)hBknm6hj@r zx0nEpt0(Uad7mr8Sq#&7H7u=7bpp#rE_J9)B&==2(V$$s`)Yu#VLKeB0N-w_kn$8po&&I)fTP@)Q*5w zdV9^j-Wnl7PvtZVDf^a*`1a$!2jv_q=9n*)=BWwUxiqcGVm$;52ks8zjb!Q7{TTm_ z-+rTNOPUvF0`@{M7jz9Tw~4DG$AULu04X0q=QLPt)YI??@A((q$qQvCm2l@-3HU!`HY77 zrKO`{v&xC%DF%k}a`RahbFm6RGDbc@Aq(^Vmdo0080EF~W?r%iB$JYrYosZVao$m^ z?zBiU6u!VBSZrNIu~SUx&*84&urJzJ0bZ>RKRedsd$JX-*7^-4Ce^<}SLmLvRDNOF z0A5=m>;)T9049jh)D+MxG)OsFX7}S5XvKbY@dk_(Me}8v^Un_FDZ7n+>#3jLD*7W+ zCAz#j2kac_jcy5vx&L+opVBT4x;m2KNH1hniM=>5o%wA~woa1C&unC&UO}pZN-%Bp z*hUbKIR2h560>qb_dBdJzZ#%ls5FZLVRizG$?UPY_Td` z0s|#Wu47&Z`%6&IdBaG_IpUkW7<{}@! zpP{X?wEr9J-T0UGL-=icP`l?;fK-3)`DTr{yd)=?9FUOni5qo=MkKGsGPf^18DGMa zWB?Z}vGRR~T@oN(w#+vNGM`^Y{=v+Vz^kjuWlaN30@UD4@!z9@1)epuqEY8!IcwoM=0>^+Su7utIBHs60xY$(>;{iFOjCg zhM$1Hn+P`T;78vSK-D!ZW9O+>fjf53Cm?q}EpDmyE^eI(nqy1d7cb9md`4?%s4h|m zRib{&6c~DV$RTa%B(+DUkfWUgNM+EiUU=ZR2B;U=&Q8bUs(-}o&P+~BPy}gxeWXhC zGL>brjId!GqyM~pUF}&SP)M0x`N#8xQI2U++`Z5tG3bAcN(Cc|L znjoE(Rb;lJM-4xCG>s2m`=<2L!9R=J*WS4#{TR+hP3;s@2(zG%bfUVbe6MC!<@ux` zPwK<#!q;Y>S;7t~pW%bQ1j$_@V{N@PV}2Slwt5V{fO@Xy@`10Prea=0Y-g&VP*K?| zsK%}&WE5&6)o5cUk|l9YW>75!!m9r`#w9j2dy@Y?*$u|gw&t9 zMdp-skYr17&!TRR{!{_e(PmfUeFxSH=9F`o7^WtoXA3XIt#>ll>Wo$Dq!eLFf7Yj79>_%L%J5EsJM-(^>=v2noqG2S5bOK87113_ZK)PzQnBmv@ z?d0HrM1_`vhx3=nX_4Q_1v%e;Y}yve2FNoWWtGeVtjN`P*iJm`Fuu{)j=VMq-*$7^{^%I-0UF=JD-YX?ZPl{&TTVl2R$#fa10$UKv?l3;NB}Q zkW)D@8HLC#6)4ZYbTcy1zCxSn3Oq0^y4%X|SC8FRj5^4h*`oCzp(SoE4T!19$ucKg z3eJ3sW6a)M`*o3A#MRNd%*ilvm~L7>1?r`??QiiUzcL;SNDW6ozi$do$_kCr_(kQ8 zg)qVG9mph9CBPK2U0If(E2>pdk^lXIWrzltMLJXp>`+Efi!`$C@{Gp(Of{~;JSchw zu!S_lX!feV9b~DWB|{nak}zsjY;Ub{Hc3dTi_{KE+R}9;dl|y|21Y8VVQ5PPf!3)D zBD-^#6zzpmb>=UAT;F<>icw@UTWMq5aPxknA!+*J4^c`$XI^Mr<0a{1>%yco?L~97 z_g#NmMD%4}OKe5pLlNi`yPiMUGqyo^!$Xdb3%Cy4n`7H4-{ycU$;~(zS+zaKvbWl4k@`@?cdC>zG(0!@~qY= z>?r4tuADbZkCZIIXm(byU*%Z)wwZ_FG>;xuAPQ3mBvm)v6fsq-qS+viwVp#oYZ1UJb zbI_BOsVn(L#h+Gh*+LJ-loXtUI;8LFBL+0+=EE_+k~ai#!@}@$gAy%&79Cc_xB8h# zAB*I6I4K5syJiXVcp>cbwc5Cv>8(^f4~ zE4$^J0wt8zh0|j&6$m-}Q|2~^$`4am)@gQL)B@W(KBRb>>Dxj`@vnPU*MKH=9#pt| z0T&((FR@>cNPT{x%p5NPepX&OB{}wIB{^hn6Z;;gzKmV9+cU;J7AOa_KpoF|te!;< zrmmhGvopwN@TjgyeNpsZ>2+owh7u;Ues1nT$y_|JDwF2w&>%4iYQ-#0yZyriULiq( zF9I5UL1M#UEb=pRusT&K&9c7n#VE_)XuDFp;B{-~&_&r~SBZz84owFw3Gb&78qbMX0P;x-GRe;YB) z^!|u~IpC?7GX)WgR>z8MG@N8hV|#};b2tu>4TQ#8>izmg^DvhxBqf~KC$UugmM1+_ z8HiCRsJ;!+&Y(zdLB8*&NP3#^M@xQYX+&0VW`5)sMp2H#8O8%dIf>-8EYQdFEa`zR zRY)V({A}T&uvlndK5h=N__?DPCnA^8x5SSz@wH09mm4(qLRgNY`v&e?Q=E1KX?a%M zUz)04p`76Lqj+ZFME&@gF z6l;F?tk0N&PWGqhd3K(_t!EK3b-O56!9@HgM$vRw(YEnz;k_bo$M#J3LCw-j$&59h zM^};Y5!-uXpXD|U4{#==Zhj`bpj2fN!s4iVQ zsC}9poyi;dk?NQF;UX`b*%g@6Qk*-$(sb`~Rv$xrGI!wPr6&@H_=0Fjsq;tj&k*o! zVV-4gN9RxRA2R?9C95FgyYnB;ZX7nbL-u)rPjUeRqqDET_X`O`R{(W!yABpoDrpd9 zoWLH)^|!37MH?jF8lVi3e*-JkFU8Nc#19W4qLSmm0G0o9e!h0DmMWrAu$=r{%ifsv z=hH92LDUuP3!=xGp>9Kif}osvugJyP(%5a0U?&Q;Aw^)%n(?P8FG=fZtInI+heP>} z`B|$=a*)UA8un|nul2WUo!dID<3Llh&WwGpAa=+(nSh+0(BM#&?fDkf|Se z#4Y!lSteaEheP&Jz9ZcC4J!J$Y`qsgQ^vr~HDVP6 zL2Id=yS(!43RVh-E&dibOT#JpAU{h>EBa_C?kV_OoBu56iOlipGzk({%x5cDm0?)z zwdoQ;mAqOkxl>0y#MDRMQgD=jO-g?C$#JtcaQfYj?Tlywh!M^HVr3t-f-D~$F{~r? zESM9z3)c#gkjFTun+aDj3^sv_t-D_VJ@7ET*K*=HoI5-u`d+lL)z54IK_l}!OP`_m zjTZNePAd-gWf@VrVnnZ|DZ>)z-<8it8Lxkl zZt0Hl8l;Qs!x;+;KFK5F#O2tWSst#L-u*vFfE2=7f8zMYGlNpWX|n+(J1L7bzyQyS zpM6P6I<0JO6*W{x)y#da71WI4JODbOM!*EHN-ee=$LX@soxx!pC zQ~dYh=XLgmRSckyF&<>J?ZhaE4W^*p4j?B7J10|a1~`7g7%q_wQ2ZH9wwRVbnz*&P zkm`i{qW;nqS*kP4YSLy#Xn#+0N|7jcm#*lV$~Mp2&n|^mSj;xe9?SWk%{6SXqDT3B zH1NtRDm{0MCY@~g1k5s+KhpLQCq{Xwb)*8ElFbwf7EKk%s_0VLQhGqMG00a93jV`& zY)ILgi~X-qAg{>0dJ<^@j-uyz_E5^yM zxJ#!4t%>xZ96dwxF%u0ZnAph>diP^ zf93)_zB#=XmfQqx3SGOh)|9BRMJe5#q-ZreHpU_q-L#@)Zx5&@^Jjz~&@ql96@Ak> zs%L5^K`O?Kkt3CZ*EWZM%Lh`M@WGwcLPC0Wl-)W8>)Y9A#O&}F*$OfZSb}nSE>^5O z=zZp0wy$oU#r2VSM7jHiUu&hk%YPkK+CpDL9Y_SW@=4w`O*PjXTAeV2(?sL~i;CDm z;$P+H)neI$_KHE^pR2RsF(19hXuobzXbRD$LeL&z8qresq<|dDDu#v$qQ7qFhIV8P zd1N4WY+~FFy>nznJlccD=K?sE4=e^4Vvthn(aa#NjY*Cs3#AJ=-%`;?r7cV5B)-td zL%My9CoK3O74EE4R6oYftp;m>uS;veCYU;J!XT)WFkB8wz2(SKVRXbl%3_Y;U_4OB zLZAGC=%QXVDzfrUe_%^_Y_9^;&=wSnd}2HxOnj84W|OMejA?=3P0KsM-@ql@mNeh6 zcL*E3l1Cm2AI&jZJk_SahsfE51CdGyi0OJtt9+eljQ$;iIQuAPlt0bR%*k9A9d+u& zCdiyWQalWTo^P9EXXdF9>9{~m@ylwo->?Ielz>fYb{xSqtWJy0?5ZHe$!@h-3lb(P zj)&CKti*eu=ctI1mgg#q2Ba9uTLKS$4ho&bOB4(dH%y+N+IWs`GtNfAMPI(e%6l3R zp^Ka>o!f^uBy}Wlw&AvNh4m$iuObBDmy{CWYz2er_McE$aeufTi14Ipu1rAys#dqa z!pkvRyeKt>vmZSM*P@nkRP=5d@Vh$9i;{2?#7CkR<)9NSuiBvGyWjcD*7*J9H$VJ$ zbgv)Ryqy|dFo@ZRHn%)Iy$?Jn1=;$@rdI{wzfG^lU0sE@^JNkvxupBe9G}MNoq)MC zST}(-Th|V@EgdLXg94iKkF4VYtn{q(%=$-$mjK*FP0nW*ZD;1!M&qxSuFdLEhl!3* zO%4Ty`cc9S5q9cvL6q0gb@ik?bY+yM4P>fM%c%Q{;6>{6t|%=F#Zd1iZ3CLP+)EOt z0yI4KYF4h^No5{Mm=m%R>eI4IK;^EPtqYKqBlEY>>zogC{Y}=gOyyB;QRPwh!Orm> z)SnFHQzkAGC@+-4OCwK!O2JDrSre;J^|Jzp%Ohs(c?q;J}{m=7=00cjAu!y%!` z8<)y7iI>Edb@+}*qWN!>Gp%jeSgGO3DcH?48chXaohest+g`8k0L&Dt zBaJ1lAq^@sS9C_0QFhR5*RsikiZLk1UWhF{iCvX)8%(4#?#dl9LHb&`4spG8WOr~| z^YStV!?%DY#}<)Zn2v7bT2gjDei&0Ku)oE2x=Q}?5M*74Hx2y)e^U6BBWo}q+(GBk6uI2pdrqc5 z+peIqqzXMDW&aDyMur<<4ZQ3N{S_&By_xK!FU(8%Wk_SKA#LmRi0tKh042Xpfea(F zf!qczLFQ8>z)Hw}l=ux|&5GXEr zp?$EZXC+-hRrir{)DE>ZnLt&lEp4s9l4Fc$szoCIB@RnrYZ+5zP2X4Tdao4b zn(BVOiPSP1dKHUGRJ1MKLy1h7VfcF)KtgBd!pFsmxt7lIh~=KYXGQ$iLOHd5lp(P1W>X5Q0%) zx<=++YZ9k#-1d{M9oe+cP5IHu1Hen)!n?Qm03oH?Iug`T8Gmu=A{y73Sq2I3A@8oX zxje0r;4(Yl(gDiqrHkm$Br_W2si2tSK?>dU<8wNy(CKV9y-jRiGSlEX;&Q8;BheGA z?W`(j(jl_20mLuR)m>tp`^c2P7f${rJjn^g zRdc;eP7o`WsMb$DO~w6qce}weT4}j;9qaFbyUx9(VohZ#L5~?Cij5;`vaqTnC)A-B zh}(1ug*K<_O%Qfr2n`rV8^q$2{ah`t+bKNUEoHJTE4$HNS;Uc0*eBHXEJnD3XjKJ+ zMr+g3;7gb2r-R;`F@VtFnEC1A$Y-Ex<|P1m2Be71hqs6X#pjga{6^V*IcltVK?6$z zjv$Jk?;q2>7XgFHWR*MjRd`hQvfLD+OCGKbdBd59%kzz{ig@s{%Z(gq$&|qBVm51e z5&gVZ7QIE*6h=W|4iLVLl4$zC-@+NB@H&pBZuJ2tOOjIblPcR9vwXtK>%cPkNyv}C z9^~Qce8pGKgOJ3ZW2;b+XHCZ<$;6$85O+Oid**{d3m>&FMW{xb0;g`e;iUR=s*Bj$ zL7l{77J0_|a=<&M$RFKcVVr9`@xq!ALLQ5XKBJN{%JyT9{ZM^#^ad0!G36y^9F;q{~ZNR#w}M3tBL11|alCLM-tK3ON@i>TyBh-eQU0uU_u> z=rN+Pg5^i4Yxz;d-r!V1qN4~SU23REC3P8JGrziajupLKs&!yxOJ6K+%t3)|J+j5B z88mVN?}`07(K4%_)@_~7Yu>XS4uYD%&b~q|NXbaAHZeJ{xp&M~5k(MkmK|Z{VAtUG z&?8IULhT?mID70^>aIzkvnBpg*^0AE1L+A@QV5Vr9i#ZfmU4v~OCI+S^zz>_P1 zwFKu$ClH?N`hZ2`yS$N)g}_%6j%y4FqxU?MYaB?jeGtXhP-`#33yEE)1f^blG_9}h zOUgdEE)R>7=xyr>jFB&i{aiYBl4;W z4tBQI7dy%J{`yL+``;FODF~5yA&|*NtfeA1ZyBgoQ@-xz5MlMzg~u>AR^E1~^m~lS zit)H}WEBdmU2gZ+mGcwI_7C5mOwq^9&py-EI*JwVS6gD*I;?-K4GG;iF@|TX!k#CrhJ>J-BTZ7+%uJG8TEKuU|YLIl6Y*cIeYJsT6AL4KVQUY z<#Mgmp}!=^75{XE?#>!o@F^Nft|EukdK<4%ES7W%^CeHtoCD>dEAd0ijLK;wZ9 zMN~m=36NoAlg;<;89%}|6M!6O-@iCj^QO@aUiZc5kBI2!FH8q-7V*0loG_T2P(Au} zJ&%$1$L2C-E-v^!M=CA%FY37Y4;8Ag$qFD4b+ZNC#}?G_HA|7-Ae&(rU1$#%96m)$ zDKpFu3%64!UxJ{trS!EU+6kPct~KY49UiL{+;}V_C(h0^%#MEJOn&QZYVX*H<~K-- zNY)6@uudN3O2he@oQA!U%u*w!H9hXgCg)mc`{kpcc+X7!##=iMQD=#mD!VgbiTF-_ z$vsxGJ{+h^sp&Q?WAze#1)DPbkm?lV#6e`xHM9@QRR?;GdnEdGHU1wfvbdRjG9MMHND%>)iaT{@a*V%u3spyj1RJvF)B4@|UPMOUR;Th2zFLt6q zH;MTetA0rzyfWzlANE6pF`dH-1$(wPvpetF@Dv_gznwZW7Gr!3m%9!ibL|kh ze=>=WIvZKTNcThc?*fDPgso$##nRH($Ni4m#Eps)n6m;Sg=>mCPaNp#&&@gze@m*R z?Yo}mfct03_-yjSYA-Wing70aV%$0e-21Mfp%LWV&w3+@oBRX@KsUn8N?W3tC~gQf zTt*$Y4$F5_Cm;~4LJAEr`fj(pXUAI5LL{|ZugP0r3=62X4V!aYUDQ9t2xY5wq%_CZ zs3LcG3yRO=ijBMu?5w(7-GVn7-2pGS^PAmOXam#)t2$zBg7X_PzF`xG(A=~RKko6a z^nwh1KCnl8XpApxaC^H01lmYSdLda!bd2a?U8n@0h%LjUzT@T#B5drk1;0oK|KSz$ zMc?}XC%X>n*rRKMeWYlz*cJVbEiV*ikZYlAgxKg0Yo3wP2>P*?~D{}6Ie zdZXnj9d!q5Ky!0p_jOL{_t7YEBBO5c`jlP=Ms(!+_jN@Q!_kEPxoJ3WHN$cKGq9Sw zWj!#2XTESe2_&+fXO%z%D_9ZkLnLLd9+ z6KZlA6e6c}eMw(%{K*kXc-U}uKmrOZ1n*~s#CuKeQdx!XnhdwOH+hV}S4MtKekTwe z;km_o6+oMh;@gWb3!MJr5#)-Gy?2KAXhvQ%a}n*z4aI;IBiDc}t z6v%!f2%Dfk2a}Srnpr{rRu5`ZIW+Q;?UrxqchY!S-8l1#jeRdp>@c({>aqILzF?Hk zClI__AF;XzadD$c`$65eXUiUKq*h>zxDn;Z;IpCM@;}-P?OlBG34cyK810@dgg*zO)Na0T>X?_^T=iQYoD8HF>*P zr>MSJ8S&<`Zlx%#vbjuh^VYKtq}*a2l149uq)x0O(Xb^y*-sA7cNi+-p3{Ly2%C48&*+F-DWueQLArD43SFUEkM&htUjFR;LTbKrQvVvwft_cO)$=HxR zTkdFc7ejUisiNr+XH5usExN@9cHW$+ATC>>=QVQP5RG2ED)ea!4M#j(08f3fHD67L zUs*k4&Xfyk^~r$9kYey5e}S4SEXgP=+4yTOmk+DQ9;R7e3djd;4(Gf*>P1BX9v6Az z*@#n;6@U70;MpZmRv#VlUMxpJwsDi%UUNc*@w|Y;ZARCL^eknDkcf(1`<<7h?vF`V#XubfQwtn*jR<% zwrau#)_~}43&fr^uW4xu4T61If76KK{T5(S8;n)WR-62x)(%FOF|!fZL*L;4YNQw#mc?Kb)B zIEY_&ut)0-pQx}@5@AC^O zlz>F6nb|?g#JdP(0w9FQU%)i=wGRAD<_qc5=WSLYxt{kJm^mG>>ezD)l{)|&LQe80 z-A&UcGaslvzqpUOk99e6H;C#GWU0}5^s3(PZC=L9leFx?ny`qKxZ+N`;4yRFP16v$ z1_ZK$gIs3sIviQ_)PElA9>Wu%o3bM#rsGJ}Bk`Wb@UBXg8Tyr(gKLdt^bRP&kl2JG zu9-hmZ9r_SX@*1}IE62fzPL(A;XkY3632J}ju5g&EpAL4>E(>*SPmt_S5;9Z0{O8c zuoBR-Pk+5I3hCzGdr<|n$t6I|CvX0UJhHQN0Ts+f7S0~8&!S32Kn5KqF$KDazJd8t zvb@7%%j;9W2#lR5^M)VNVA`?N9bv_8P&YUi9f$ZNh?}yInvxe8>zZDMM9C@{hyh=K z8NlRdVvLXA z0A5$*3HlLZGSw&Ia3p^za6oswlIXk>>eblzhwkDH^g->EX1gy6PZcPb^-A$C( zR`GQZs`*DE)V)x8_K6tJen!`<3av*UsJSD7-2Zew-yWwJ)6GF#o9Ww`QJeip-xF&{ zdi!>ZAPs5N*`GtKd=W4DEMQ(vC9lLFiOw|~hw!r*pFcvqz={wGB7 zEm!ZQFi44d}x#elm(b4jrsTp+%(jVkUgG`y;d0$z6i+z zwi2kKg8FQUq`3a~4I>HuiwYOKjy(^+=>wQH1WJoodhn4+ET&*Pkq$o=XdTH8@IJ8R z{|O2=$$s3r@s7*mExYa!hyed^?a6zi`I==o+zQsw-HpfjktE9 znm$b&rhNgg6+&Ax0#xJ$5v|k-_fDNwZcjW8$e)&X_vM6MuQuAkc?TTT<7f+c%PQ(% z=)f?jz|HL1ByiNkmgK~hbjKFWs*Sqdl?;3xr5K?NY9eLdOK&J=%O)CI$+tXN9Z2vI z%*eribfJAJKtPq-#3)}a<~AJv(A*zw^ofTx3N~@Hmzb-zoraDCrTF=aVctke7J^7C zH=c+hVRb8b$WvZCd-K;36NX3QbE6^N*H{Ifzyx-XSr!u#A{s6w-@M^J^`&$<)Q*of zAZoDm;Rka0vkC(!5|7Lryo+3fodX^WMQ72iy$_^5xI6RZ2M{s>bU zv|O#l?jAc^iGWK2btA=Ht{9wW8T<%q?yq$69W8L5-I+25i9g&j15D~`9&apP2eKdW zz8PFLHDmoM0Ebaq-)rW(uUC2OCO$Cl3h4P6N$+i48XjMVczE=Jos*&uhCM%_wOf2P6%*^2 zM=A+L7SLUT!0W<)eoCV69`tkGGb1iEdyk>LBuCeRI_AUYrzwDiBNHbHj<1Y>T!Smh z?lu8{0d?MJ|GoQj#dOLxf&os3sb!X0`W*7k;!lOIp$y+_LL3mQ-Jffw?f-klzn+^( zs1bT*0Kg)=w*R>b{M>}+n1z#YdxO*tt1!g%0}LpGHpn1_aUQq8gUT{tvuBJrrSM$n zcc6h6VEJ27yiB4U4qtrsX-}GW#oqGS#RccW-oYql;Ys4I=_~`19g&w#QJJM%O`vh= z&;|EkTm`s)bMm6>;5cL0ALBWqAcVfE!U;rTZz7(WbdGWC`8Lh??Q2i&uGzwoRGGi% zb3#>sRPVEfK(U*h5T0+gezEM;&V>*(EAAw;Lo>}U$ZERJ+>$Ln$G=OnWD(^rw#E7q zS1h&#Q1`AxYEe}MTV!+Fhax6Zq#!@_p1le93C&NuuYMvvhtN`zgt)zt`?2ZFvWPJC z1RF#uZDiJk8w|5Zvq=XsfLRImt874ssNF|`f@I3A$-)VYoLM0oXa36@tGIf{2LUxe8KZ{y(nnGAeE^Y7}sBDYQk3ySqzqcXxO9 z;zd)6yEC}kKyewI7Iz)oTBLY!igS6}?^}1>`|qsDo^y6iGLx(%`w5jBS_7pcs3Na2 z&%%R5jGhB|vAjp|^0e;K);9*FcVw9u$wWroMh55F!N%gy5O~zrb2g(9CkZFJYrm5b zyYv>_es8_gUT>ID{gqAPQjsh`f$aJ_hfZF=5EU{LfW-8z>#|@S`{;9q^19{$>6@~X z+#VQBGfT(NqaMev2~`ZmV8MmYLIU}u=PX&8;f|v;Ck3q;>I!C_QNs;jza=hAu88wB zNB+UMZ%iEcaR7ZvMO-m|C|{&Kx(9j;xMb@jTWHgmYp+6=+hBsGr|N5p)5mo&%UI4u zz9T^%j0^oe-t4hz8{WF2Mdju^q24&@67RRm$ehr7W~OHtd%sxXSf7(Amq8pKc+`Z= zFfo1+qS#JK(^sVE61RAG{qZ6GxzQA6M4G39QAYDs_=E z0g!xW-k$BBa)}TK?{KQ35%uD+n%nu)5PpldO1@#z9)c=9m*KKrQz zlhdVu;}O6<&g{Kqmc2@5^JGL#V){gI{IGI!JhW`brV$w*==e%;)B99_9LxC&>8%l+ zEYqc+qN6ubRA|8wMO>O-e(&> zkB)-So?b;s3>#~!lJr3rl-ET!cOvp7Kzxp``4xeta~@2O^yP6<3bAmo*OMQ%$A0!% zc%2Ua^&Qd^@{6U=x9qmOOz33Bibhq?KQD1D_9@kTz4^gM^L&sBFht3tr2^E?1Cm6j zZ%L`1i$+#iZ}W&CG=FHh)a6u#V)2otxev1Vv0r&4UfYZfB4v8rzwLQ7^jpd4A$~pP zGcuqrTc-|=eG|JqNhGO9kA;JzOL^N+k|Y9=^|a?gJ1R9i0+eNPt)TJ0Ullb5=63&p zpXQ&pSY*%1rthja^o_CD?uv*9-HgWZUA)b5mwdxh-h?ZezP>u18O4 zcNMsFvFYO1M2|(&&&cC|vz2UWpn>-}pTP5We&rxd>)g?ay7F`4r=zQNJz7dvGqmGP zT;I~l=iDjj?Mdmq-p7RC>QOeXpV7c4&M}GGhM4fF2zwabwl{~<#T#Rge7XAfEMmz! z_Kj68GQodH0tt?OmGv42A_|5}|IWMBvz2`$68>e=3*7Lm7HEw1pHitp}uj=|Q0m$(5;7HMT2 z_~=GWw2ar-=yqxfHo)w!mA*Ybcv^j0!{#1!-gGfF!nwLU$^>Ge+b;YC?d($2qSaeZpQHZ9^1lKxEq(@HUQO1K@FGqMJ74FUKI%E+<5 zk^X8KeolI@kOrvI2Ekg}T#Y#*L9}WaaE}ghaZ@*CoecfXv8b_ED+S9au1Qn6q}B!k z6iZaXDbVzEQgn;g)MA==eg-7e^uFWDVheO*%2KUotMKMYd_#Og3hq5eHfCP>gaSDn z3h$Fs-zBGpE#X%kDUJX}rnpA$tZhPf3Xyo}Rx zHU?Po#|RJ_Ge6IVEQMAbQH&TBxH-^!OQjpFj<6K0&aD)*5Z5QVvGjkhpVPWE^U5He zJ4#6CrNB(eQ7>0fH8Un!-uci7nVTXwGS2_hNikOKXrZI_%U1xvn)dcge=LlBOyUSw zDPXm}>p9Bg@nYWLazbj2c8UiPE>NcsVu*LdYV&2@}9Lw>=rfa`}3G}5nB0nO+CT(J@

uN{~C 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 From 3c6280b11eb7050e07dde6432ee773e5f50ba988 Mon Sep 17 00:00:00 2001 From: Aurora <21148213+aurorasmiles@users.noreply.github.com> Date: Sat, 10 Oct 2020 11:57:12 +0200 Subject: [PATCH 3/8] Re-Implement //regen (#598) * start reimplementing regen command * start reimplementing regen command * Formatting and logic tweaks. Regen will now throw exceptions but they are not caught yet. ConversionSessions will now be closed when no longer being used instead of left open. * fix //regen crashing server * added //regen support for 1.16.1 and 1.15.2 * cleanup * Update the issue template * improve performance of regen by a factor of 40, approx 1.2 millon blocks/second * Update the issue template * Update the issue template & add a config (#640) * Update the issue template * Add a config.yml to the issue template * improve performance of regen by a factor of 40, approx 1.2 millon blocks/second * Fix entity rotation (#642) * fix entity rotation fixes #577 Co-authored-by: wea_ondara * Fix toggle permission (#644) * Fixes #529 * fix superperms perm toggling Co-authored-by: @weaondara * Fix #647 * Squash errors and debug to aid fixing #652 properly * cleanup imports * cleanup imports 2 * cleanup imports 3 * cleanup imports 4 * add patch by @SirYwell * aysnc world gen with features and stuff and some minor issues * optimizations, full chunkstatus, block populators * optimizations, cleanup * optimizations * fix feature regeneration, fix temp folder deletion * cleanup * make chunk gen multithreaded * fix precomputation of chunk lists for RegionLimitedWorldAccess again * added regenerator abstraction, fix aioobe while running through chunk stati * remove override for getChunkAt in freshnmsworld * don't use concurrent chunk gen if custom chunk generators do not support it * distinct between generator types * improve regen speed for overworlds * mix * Add message that regen might take a while * use a shared map for FastAreaLazy, cleanup imports * use custom concurrency levels for chunk stati and process accordingly * implement new regen in 1.15.2 and 1.16.1 as well Conflicts: worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java * woops * further abstraction, finalized regen impl * Formatting * Fix some typos, remove debug * replace wildcard imports * cleanup debug * braces * serr -> logger * move regen impls to seperate classes * fix world init for 1.16.1 * fix world init for 1.15.2 * fix world init for 1.15.2 #2 * use original world name for regeneration * Update Regen_v1_15_R2.java * Update worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java Co-authored-by: Matt <4009945+MattBDev@users.noreply.github.com> * Update worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java Co-authored-by: Matt <4009945+MattBDev@users.noreply.github.com> * Update worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java Co-authored-by: Matt <4009945+MattBDev@users.noreply.github.com> * improve documentation, use parallel task count for fawe settings * fix compile Co-authored-by: wea_ondara Co-authored-by: MattBDev <4009945+MattBDev@users.noreply.github.com> Co-authored-by: dordsor21 --- .../sk89q/worldedit/bukkit/BukkitWorld.java | 51 +- .../bukkit/adapter/BukkitImplAdapter.java | 10 +- .../worldedit/bukkit/adapter/Regenerator.java | 545 ++++++++++++++++++ .../adapter/impl/FAWE_Spigot_v1_15_R2.java | 107 +--- .../adapter/impl/FAWE_Spigot_v1_16_R1.java | 89 +-- .../adapter/impl/FAWE_Spigot_v1_16_R2.java | 87 +-- .../adapter/impl/regen/Regen_v1_15_R2.java | 478 +++++++++++++++ .../adapter/impl/regen/Regen_v1_16_R1.java | 519 +++++++++++++++++ .../adapter/impl/regen/Regen_v1_16_R2.java | 529 +++++++++++++++++ .../worldedit/command/RegionCommands.java | 1 + .../com/sk89q/worldedit/extent/Extent.java | 2 +- .../com/sk89q/worldedit/world/NullWorld.java | 5 + .../java/com/sk89q/worldedit/world/World.java | 41 +- .../src/main/resources/lang/strings.json | 2 + 14 files changed, 2155 insertions(+), 311 deletions(-) create mode 100644 worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/Regenerator.java create mode 100644 worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_15_R2.java create mode 100644 worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_16_R1.java create mode 100644 worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_16_R2.java diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index c88de2a76..f282901c1 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -32,6 +32,7 @@ import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.wna.WorldNativeAccess; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; @@ -42,6 +43,7 @@ import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.world.AbstractWorld; +import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -202,61 +204,18 @@ public class BukkitWorld extends AbstractWorld { } @Override - public boolean regenerate(Region region, EditSession editSession) { + public boolean regenerate(Region region, Extent extent, RegenOptions options) { BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); try { if (adapter != null) { - return adapter.regenerate(getWorld(), region, editSession); + return adapter.regenerate(getWorld(), region, extent, options); } else { throw new UnsupportedOperationException("Missing BukkitImplAdapater for this version."); } } catch (Exception e) { logger.warn("Regeneration via adapter failed.", e); + return false; } - /* - BaseBlock[] history = new BaseBlock[16 * 16 * (getMaxY() + 1)]; - - for (BlockVector2 chunk : region.getChunks()) { - BlockVector3 min = BlockVector3.at(chunk.getBlockX() * 16, 0, chunk.getBlockZ() * 16); - - // First save all the blocks inside - for (int x = 0; x < 16; ++x) { - for (int y = 0; y < (getMaxY() + 1); ++y) { - for (int z = 0; z < 16; ++z) { - BlockVector3 pt = min.add(x, y, z); - int index = y * 16 * 16 + z * 16 + x; - history[index] = editSession.getFullBlock(pt); - } - } - } - - try { - getWorld().regenerateChunk(chunk.getBlockX(), chunk.getBlockZ()); - } catch (Throwable t) { - logger.warn("Chunk generation via Bukkit raised an error", t); - } - - // Then restore - for (int x = 0; x < 16; ++x) { - for (int y = 0; y < (getMaxY() + 1); ++y) { - for (int z = 0; z < 16; ++z) { - BlockVector3 pt = min.add(x, y, z); - int index = y * 16 * 16 + z * 16 + x; - - // We have to restore the block if it was outside - if (!region.contains(pt)) { - editSession.smartSetBlock(pt, history[index]); - } else { // Otherwise fool with history - editSession.getChangeSet().add(new BlockChange(pt, history[index], editSession.getFullBlock(pt))); - } - } - } - } - } - - return true; - */ - return editSession.regenerate(region); } /** diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java index f94dc5e12..cd3e86499 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java @@ -25,11 +25,11 @@ import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.boydti.fawe.bukkit.FaweBukkit; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.wna.WorldNativeAccess; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; @@ -37,6 +37,7 @@ import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.world.DataFixer; +import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; @@ -236,11 +237,12 @@ public interface BukkitImplAdapter extends IBukkitAdapter { * Regenerate a region in the given world, so it appears "as new". * @param world the world to regen in * @param region the region to regen - * @param session the session to use for setting blocks + * @param extent the extent to use for setting blocks + * @param options the regeneration options * @return true on success, false on failure */ - default boolean regenerate(org.bukkit.World world, Region region, EditSession session) { - return session.regenerate(region); + default boolean regenerate(World world, Region region, Extent extent, RegenOptions options) throws Exception{ + throw new UnsupportedOperationException("This adapter does not support regeneration."); } default IChunkGet get(World world, int chunkX, int chunkZ) { diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/Regenerator.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/Regenerator.java new file mode 100644 index 000000000..cbf099142 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/Regenerator.java @@ -0,0 +1,545 @@ +package com.sk89q.worldedit.bukkit.adapter; + +import com.boydti.fawe.beta.IChunkCache; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.implementation.queue.SingleThreadQueueExtent; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.util.MathMan; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.world.RegenOptions; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.stream.Collectors; +import org.bukkit.generator.BlockPopulator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Represents an abstract regeneration handler. + * @param the type of the {@Code IChunkAccess} of the current Minecraft implementation + * @param the type of the {@Code ProtoChunk} of the current Minecraft implementation + * @param the type of the {@Code Chunk} of the current Minecraft implementation + * @param the type of the {@Code ChunkStatusWrapper} wrapping the {@Code ChunkStatus} enum + */ +public abstract class Regenerator> { + + public static final Logger logger = LoggerFactory.getLogger(Regenerator.class); + + protected final org.bukkit.World originalBukkitWorld; + protected final Region region; + protected final Extent target; + protected final RegenOptions options; + + //runtime + protected final Map chunkStati = new LinkedHashMap<>(); + protected boolean generateConcurrent = true; + protected long seed; + + private final Long2ObjectLinkedOpenHashMap protoChunks = new Long2ObjectLinkedOpenHashMap<>(); + private final Long2ObjectOpenHashMap chunks = new Long2ObjectOpenHashMap<>(); + private ExecutorService executor; + private SingleThreadQueueExtent source; + + /** + * Initializes an abstract regeneration handler. + * @param originalBukkitWorld the Bukkit world containing all the information on how to regenerate the {code Region} + * @param region the selection to regenerate + * @param target the target {@code Extent} to paste the regenerated blocks into + * @param options the options to used while regenerating and pasting into the target {@code Extent} + */ + public Regenerator(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) { + this.originalBukkitWorld = originalBukkitWorld; + this.region = region; + this.target = target; + this.options = options; + } + + /** + * Regenerates the selected {@code Region}. + * @return whether or not the regeneration process was successful + * @throws Exception when something goes terribly wrong + */ + public boolean regenerate() throws Exception { + if (!prepare()) { + return false; + } + + try { + if (!initNewWorld()) { + cleanup0(); + return false; + } + } catch (Exception e) { + cleanup0(); + throw e; + } + + try { + if (!generate()) { + cleanup0(); + return false; + } + } catch (Exception e) { + cleanup0(); + throw e; + } + + try { + copyToWorld(); + } catch (Exception e) { + cleanup0(); + throw e; + } + + cleanup0(); + return true; + } + + /** + * Returns the {@code ProtoChunk} at the given chunk coordinates. + * @param x the chunk x coordinate + * @param z the chunk z coordinate + * @return the {@code ProtoChunk} at the given chunk coordinates or null if it is not part of the regeneration process or has not been initialized yet. + */ + protected ProtoChunk getProtoChunkAt(int x, int z) { + return protoChunks.get(MathMan.pairInt(x, z)); + } + + /** + * Returns the {@code Chunk} at the given chunk coordinates. + * @param x the chunk x coordinate + * @param z the chunk z coordinate + * @return the {@code Chunk} at the given chunk coordinates or null if it is not part of the regeneration process or has not been converted yet. + */ + protected Chunk getChunkAt(int x, int z) { + return chunks.get(MathMan.pairInt(x, z)); + } + + private boolean generate() throws Exception { + if (generateConcurrent) { + //Using concurrent chunk generation + executor = Executors.newFixedThreadPool(Settings.IMP.QUEUE.PARALLEL_THREADS); + } // else using sequential chunk generation, concurrent not supported + + //TODO: can we get that required radius down without affecting chunk generation (e.g. strucures, features, ...)? + //for now it is working well and fast, if we are bored in the future we could do the research (a lot of it) to reduce the border radius + + //generate chunk coords lists with a certain radius + Int2ObjectOpenHashMap> chunkCoordsForRadius = new Int2ObjectOpenHashMap<>(); + chunkStati.keySet().stream().map(ChunkStatusWrapper::requiredNeigborChunkRadius0).distinct().forEach(radius -> { + if (radius == -1) //ignore ChunkStatus.EMPTY + return; + int border = 16 - radius; //9 = 8 + 1, 8: max border radius used in chunk stages, 1: need 1 extra chunk for chunk features to generate at the border of the region + chunkCoordsForRadius.put(radius, getChunkCoordsRegen(region, border)); + }); + + //create chunks + for (Long xz : chunkCoordsForRadius.get(0)) { + ProtoChunk chunk = createProtoChunk(MathMan.unpairIntX(xz), MathMan.unpairIntY(xz)); + protoChunks.put(xz, chunk); + } + + //generate lists for RegionLimitedWorldAccess, need to be square with odd length (e.g. 17x17), 17 = 1 middle chunk + 8 border chunks * 2 + Int2ObjectOpenHashMap>> worldlimits = new Int2ObjectOpenHashMap<>(); + chunkStati.keySet().stream().map(ChunkStatusWrapper::requiredNeigborChunkRadius0).distinct().forEach(radius -> { + if (radius == -1) //ignore ChunkStatus.EMPTY + return; + Long2ObjectOpenHashMap> map = new Long2ObjectOpenHashMap<>(); + for (Long xz : chunkCoordsForRadius.get(radius)) { + int x = MathMan.unpairIntX(xz); + int z = MathMan.unpairIntY(xz); + List l = new ArrayList<>((radius + 1 + radius) * (radius + 1 + radius)); + for (int zz = z - radius; zz <= z + radius; zz++) { //order is important, first z then x + for (int xx = x - radius; xx <= x + radius; xx++) { + l.add(protoChunks.get(MathMan.pairInt(xx, zz))); + } + } + map.put(xz, l); + } + worldlimits.put(radius, map); + }); + + //run generation tasks exluding FULL chunk status + for (Map.Entry entry : chunkStati.entrySet()) { + ChunkStatus chunkStatus = entry.getKey(); + int radius = chunkStatus.requiredNeigborChunkRadius0(); + + List coords = chunkCoordsForRadius.get(radius); + if (this.generateConcurrent && entry.getValue() == Concurrency.RADIUS) { + SequentialTasks>> tasks = getChunkStatusTaskRows(coords, radius); + for (ConcurrentTasks> para : tasks) { + List scheduled = new ArrayList<>(tasks.size()); + for (SequentialTasks row : para) { + scheduled.add((Callable) () -> { + for (Long xz : row) { + chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz)); + } + return null; + }); + } + try { + List futures = executor.invokeAll(scheduled); + for (Future future : futures) { + future.get(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } else if (this.generateConcurrent && entry.getValue() == Concurrency.FULL) { + // every chunk can be processed individually + List scheduled = new ArrayList(coords.size()); + for (long xz : coords) { + scheduled.add((Callable) () -> { + chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz)); + return null; + }); + } + try { + List futures = executor.invokeAll(scheduled); + for (Future future : futures) { + future.get(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } else { // Concurrency.NONE or generateConcurrent == false + // run sequential + for (long xz : coords) { + chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz)); + } + } + } + + //convert to proper chunks + for (Long xz : chunkCoordsForRadius.get(0)) { + ProtoChunk proto = protoChunks.get(xz); + chunks.put(xz, createChunk(proto)); + } + + //final chunkstatus + ChunkStatus FULL = getFullChunkStatus(); + for (Long xz : chunkCoordsForRadius.get(0)) { //FULL.requiredNeighbourChunkRadius() == 0! + Chunk chunk = chunks.get(xz); + FULL.processChunkSave(xz, Arrays.asList(chunk)); + } + + //populate + List populators = getBlockPopulators(); + for (Long xz : chunkCoordsForRadius.get(0)) { + int x = MathMan.unpairIntX(xz); + int z = MathMan.unpairIntY(xz); + + //prepare chunk seed + Random random = getChunkRandom(seed, x, z); + + //actually populate + Chunk c = chunks.get(xz); + populators.forEach(pop -> { + populate(c, random, pop); + }); + } + + source = new SingleThreadQueueExtent(); + source.init(null, initSourceQueueCache(), null); + return true; + } + + private void copyToWorld() { + //Setting Blocks + long start = System.currentTimeMillis(); + boolean genbiomes = options.shouldRegenBiomes(); + for (BlockVector3 vec : region) { + target.setBlock(vec, source.getBlock(vec)); + if (genbiomes) { + target.setBiome(vec, source.getBiome(vec)); + } +// realExtent.setSkyLight(vec, extent.getSkyLight(vec)); +// realExtent.setBlockLight(vec, extent.getBrightness(vec)); + } + } + + private void cleanup0() { + if (executor != null) { + executor.shutdownNow(); + } + cleanup(); + } + + //functions to be implemented by sub class + /** + *

Implement the preparation process in here. DO NOT instanciate any variable here that require the cleanup function. This function is for gathering further information before initializing a new + * world.

+ * + *

Fields required to be initialized: chunkStati, seed

+ *

For chunkStati also see {code ChunkStatusWrapper}.

+ * + * @return whether or not the preparation process was successful + */ + protected abstract boolean prepare(); + + /** + * Implement the creation of the seperate world in here. + * + * Fields required to be initialized: generateConcurrent + * + * @return true if everything went fine, otherwise false. When false is returned the Regenerator halts the regeneration process and calls the cleanup function. + * @throws java.lang.Exception When the implementation of this method throws and exception the Regenerator halts the regeneration process and calls the cleanup function. + */ + protected abstract boolean initNewWorld() throws Exception; + + /** + * Implement the cleanup of all the mess that is created during the regeneration process (initNewWorld() and generate()).This function must not throw any exceptions. + */ + protected abstract void cleanup(); + + //functions to implement by sub class - regenate related + /** + * Implement the initialization of a {@code ProtoChunk} here. + * + * @param x the x coorinate of the {@code ProtoChunk} to create + * @param z the z coorinate of the {@code ProtoChunk} to create + * @return an initialized {@code ProtoChunk} + */ + protected abstract ProtoChunk createProtoChunk(int x, int z); + + /** + * Implement the convertion of a {@code ProtoChunk} to a {@code Chunk} here. + * + * @param protoChunk the {@code ProtoChunk} to be converted to a {@code Chunk} + * @return the converted {@code Chunk} + */ + protected abstract Chunk createChunk(ProtoChunk protoChunk); + + /** + * Return the {@code ChunkStatus.FULL} here. + * ChunkStatus.FULL is the last step of vanilla chunk generation. + * + * @return {@code ChunkStatus.FULL} + */ + protected abstract ChunkStatus getFullChunkStatus(); + + /** + * Return a list of {@code BlockPopulator} used to populate the original world here. + * + * @return {@code ChunkStatus.FULL} + */ + protected abstract List getBlockPopulators(); + + /** + * Implement the population of the {@code Chunk} with the given chunk random and {@code BlockPopulator} here. + * + * @param chunk the {@code Chunk} to populate + * @param random the chunk random to use for population + * @param pop the {@code BlockPopulator} to use + */ + protected abstract void populate(Chunk chunk, Random random, BlockPopulator pop); + + /** + * Implement the initialization an {@code IChunkCache} here. Use will need the {@code getChunkAt} function + * @return an initialized {@code IChunkCache} + */ + protected abstract IChunkCache initSourceQueueCache(); + + //algorithms + private List getChunkCoordsRegen(Region region, int border) { //needs to be square num of chunks + BlockVector3 oldMin = region.getMinimumPoint(); + BlockVector3 newMin = BlockVector3.at((oldMin.getX() >> 4 << 4) - border * 16, oldMin.getY(), (oldMin.getZ() >> 4 << 4) - border * 16); + BlockVector3 oldMax = region.getMaximumPoint(); + BlockVector3 newMax = BlockVector3.at((oldMax.getX() >> 4 << 4) + (border + 1) * 16 - 1, oldMax.getY(), (oldMax.getZ() >> 4 << 4) + (border + 1) * 16 - 1); + Region adjustedRegion = new CuboidRegion(newMin, newMax); + return adjustedRegion.getChunks().stream() + .map(c -> BlockVector2.at(c.getX(), c.getZ())) + .sorted(Comparator.comparingInt(c -> c.getZ()).thenComparingInt(c -> c.getX())) //needed for RegionLimitedWorldAccess + .map(c -> MathMan.pairInt(c.getX(), c.getZ())) + .collect(Collectors.toList()); + } + + /** + * Creates a list of chunkcoord rows that may be executed concurrently + * + * @param allcoords the coords that should be sorted into rows, must be sorted by z and x + * @param requiredNeighborChunkRadius the radius of neighbor chunks that may not be written to conccurently (ChunkStatus.requiredNeighborRadius) + * @return a list of chunkcoords rows that may be executed concurrently + */ + private SequentialTasks>> getChunkStatusTaskRows(List allcoords, int requiredNeighborChunkRadius) { + int requiredneighbors = Math.max(0, requiredNeighborChunkRadius); + + int minx = allcoords.isEmpty() ? 0 : MathMan.unpairIntX(allcoords.get(0)); + int maxx = allcoords.isEmpty() ? 0 : MathMan.unpairIntX(allcoords.get(allcoords.size() - 1)); + int minz = allcoords.isEmpty() ? 0 : MathMan.unpairIntY(allcoords.get(0)); + int maxz = allcoords.isEmpty() ? 0 : MathMan.unpairIntY(allcoords.get(allcoords.size() - 1)); + SequentialTasks>> tasks; + if (maxz - minz > maxx - minx) { + int numlists = Math.min(requiredneighbors * 2 + 1, maxx - minx + 1); + + Int2ObjectOpenHashMap> byx = new Int2ObjectOpenHashMap(); + int expectedListLength = (allcoords.size() + 1) / (maxx - minx); + + //init lists + for (int i = minx; i <= maxx; i++) { + byx.put(i, new SequentialTasks(expectedListLength)); + } + + //sort into lists by x coord + for (Long xz : allcoords) { + byx.get(MathMan.unpairIntX(xz)).add(xz); + } + + //create parallel tasks + tasks = new SequentialTasks(numlists); + for (int offset = 0; offset < numlists; offset++) { + ConcurrentTasks> para = new ConcurrentTasks((maxz - minz + 1) / numlists + 1); + for (int i = 0; minx + i * numlists + offset <= maxx; i++) + para.add(byx.get(minx + i * numlists + offset)); + tasks.add(para); + } + } else { + int numlists = Math.min(requiredneighbors * 2 + 1, maxz - minz + 1); + + Int2ObjectOpenHashMap> byz = new Int2ObjectOpenHashMap(); + int expectedListLength = (allcoords.size() + 1) / (maxz - minz); + + //init lists + for (int i = minz; i <= maxz; i++) { + byz.put(i, new SequentialTasks(expectedListLength)); + } + + //sort into lists by x coord + for (Long xz : allcoords) { + byz.get(MathMan.unpairIntY(xz)).add(xz); + } + + //create parallel tasks + tasks = new SequentialTasks(numlists); + for (int offset = 0; offset < numlists; offset++) { + ConcurrentTasks> para = new ConcurrentTasks((maxx - minx + 1) / numlists + 1); + for (int i = 0; minz + i * numlists + offset <= maxz; i++) + para.add(byz.get(minz + i * numlists + offset)); + tasks.add(para); + } + } + + return tasks; + } + + private static Random getChunkRandom(long worldseed, int x, int z) { + Random random = new Random(); + random.setSeed(worldseed); + long xRand = random.nextLong() / 2L * 2L + 1L; + long zRand = random.nextLong() / 2L * 2L + 1L; + random.setSeed((long) x * xRand + (long) z * zRand ^ worldseed); + return random; + } + + //classes + /** + * This class is used to wrap the ChunkStatus of the current Minecraft implementation and as the implementation to execute a chunk generation step. + * @param the IChunkAccess class of the current Minecraft implementation + */ + public static abstract class ChunkStatusWrapper { + + /** + * Return the required neighbor chunk radius the wrapped {@code ChunkStatus} requires. + * + * @return the radius of required neighbor chunks + */ + public abstract int requiredNeigborChunkRadius(); + + int requiredNeigborChunkRadius0() { + return Math.max(0, requiredNeigborChunkRadius()); + } + + /** + * Return the name of the wrapped {@code ChunkStatus}. + * + * @return the radius of required neighbor chunks + */ + public abstract String name(); + + /** + * Return the name of the wrapped {@code ChunkStatus}. + * + * @param xz represents the chunk coordinates of the chunk to process as denoted by {@code MathMan} + * @param accessibleChunks a list of chunks that will be used during the execution of the wrapped {@code ChunkStatus}. + * This list is order in the correct order required by the {@code ChunkStatus}, unless Mojang suddenly decides to do things differently. + */ + public abstract void processChunk(Long xz, List accessibleChunks); + + void processChunkSave(Long xz, List accessibleChunks) { + try { + processChunk(xz, accessibleChunks); + } catch (Exception e) { + logger.error("Error while running " + name() + " on chunk " + MathMan.unpairIntX(xz) + "/" + MathMan.unpairIntY(xz), e); + } + } + } + + public enum Concurrency { + FULL, + RADIUS, + NONE + } + + public static class SequentialTasks extends Tasks { + + public SequentialTasks(int expectedsize) { + super(expectedsize); + } + } + + public static class ConcurrentTasks extends Tasks { + + public ConcurrentTasks(int expectedsize) { + super(expectedsize); + } + } + + public static class Tasks implements Iterable { + + private final List tasks; + + public Tasks(int expectedsize) { + tasks = new ArrayList(expectedsize); + } + + public void add(T task) { + tasks.add(task); + } + + public List list() { + return tasks; + } + + public int size() { + return tasks.size(); + } + + @Override + public Iterator iterator() { + return tasks.iterator(); + } + + @Override + public String toString() { + return tasks.toString(); + } + } +} diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java index 232e22c51..e97f894cd 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java @@ -22,36 +22,33 @@ package com.sk89q.worldedit.bukkit.adapter.impl; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.IChunkGet; -import com.boydti.fawe.beta.IQueueChunk; -import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.implementation.packet.ChunkPacket; -import com.boydti.fawe.beta.implementation.queue.SingleThreadQueueExtent; import com.boydti.fawe.bukkit.adapter.mc1_15_2.BlockMaterial_1_15_2; import com.boydti.fawe.bukkit.adapter.mc1_15_2.BukkitAdapter_1_15_2; import com.boydti.fawe.bukkit.adapter.mc1_15_2.BukkitGetBlocks_1_15_2; import com.boydti.fawe.bukkit.adapter.mc1_15_2.FAWEWorldNativeAccess_1_15_2; import com.boydti.fawe.bukkit.adapter.mc1_15_2.MapChunkUtil_1_15_2; import com.boydti.fawe.bukkit.adapter.mc1_15_2.nbt.LazyCompoundTag_1_15_2; -import com.google.common.io.Files; +import com.google.common.base.Preconditions; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.TileEntityBlock; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.regen.Regen_v1_15_R2; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.LazyBaseEntity; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.wna.WorldNativeAccess; -import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; @@ -66,7 +63,6 @@ import net.minecraft.server.v1_15_R1.Block; import net.minecraft.server.v1_15_R1.BlockPosition; import net.minecraft.server.v1_15_R1.Chunk; import net.minecraft.server.v1_15_R1.ChunkCoordIntPair; -import net.minecraft.server.v1_15_R1.ChunkProviderServer; import net.minecraft.server.v1_15_R1.ChunkSection; import net.minecraft.server.v1_15_R1.Entity; import net.minecraft.server.v1_15_R1.EntityPlayer; @@ -75,7 +71,6 @@ import net.minecraft.server.v1_15_R1.IBlockData; import net.minecraft.server.v1_15_R1.IRegistry; import net.minecraft.server.v1_15_R1.ItemStack; import net.minecraft.server.v1_15_R1.MinecraftKey; -import net.minecraft.server.v1_15_R1.MinecraftServer; import net.minecraft.server.v1_15_R1.NBTBase; import net.minecraft.server.v1_15_R1.NBTTagCompound; import net.minecraft.server.v1_15_R1.NBTTagInt; @@ -83,12 +78,9 @@ import net.minecraft.server.v1_15_R1.PacketPlayOutMapChunk; import net.minecraft.server.v1_15_R1.PlayerChunk; import net.minecraft.server.v1_15_R1.TileEntity; import net.minecraft.server.v1_15_R1.World; -import net.minecraft.server.v1_15_R1.WorldData; -import net.minecraft.server.v1_15_R1.WorldNBTStorage; import net.minecraft.server.v1_15_R1.WorldServer; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.World.Environment; import org.bukkit.block.data.BlockData; import org.bukkit.craftbukkit.v1_15_R1.CraftChunk; import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; @@ -98,17 +90,11 @@ import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity; import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; import org.bukkit.entity.Player; -import org.bukkit.generator.ChunkGenerator; -import java.io.File; -import java.io.IOException; import java.lang.ref.WeakReference; import java.util.Map; import java.util.OptionalInt; import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; import java.util.function.Supplier; import java.util.stream.Stream; import javax.annotation.Nullable; @@ -119,7 +105,6 @@ import static org.slf4j.LoggerFactory.getLogger; public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter { private final Spigot_v1_15_R2 parent; private char[] ibdToStateOrdinal; - // ------------------------------------------------------------------------ // Code that may break between versions of Minecraft // ------------------------------------------------------------------------ @@ -157,8 +142,6 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I public BlockMaterial getMaterial(BlockState state) { IBlockData bs = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState(); return new BlockMaterial_1_15_2(bs.getBlock(), bs); - - } public Block getBlock(BlockType blockType) { @@ -168,7 +151,7 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I @SuppressWarnings("deprecation") @Override public BaseBlock getBlock(Location location) { - checkNotNull(location); + Preconditions.checkNotNull(location); CraftWorld craftWorld = ((CraftWorld) location.getWorld()); int x = location.getBlockX(); @@ -272,7 +255,7 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I @Override public BaseEntity getEntity(org.bukkit.entity.Entity entity) { - checkNotNull(entity); + Preconditions.checkNotNull(entity); CraftEntity craftEntity = ((CraftEntity) entity); Entity mcEntity = craftEntity.getHandle(); @@ -417,83 +400,9 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I } return parent.fromNative(foreign); } - @Override - public boolean regenerate(org.bukkit.World world, Region region, EditSession editSession) { - WorldServer originalWorld = ((CraftWorld) world).getHandle(); - ChunkProviderServer provider = originalWorld.getChunkProvider(); - if (!(provider instanceof ChunkProviderServer)) { - return false; - } - - File saveFolder = Files.createTempDir(); - // register this just in case something goes wrong - // normally it should be deleted at the end of this method - saveFolder.deleteOnExit(); - try { - MinecraftServer server = originalWorld.getServer().getServer(); - WorldNBTStorage originalDataManager = originalWorld.getDataManager(); - WorldNBTStorage saveHandler = new WorldNBTStorage(saveFolder, originalDataManager.getDirectory().getName(), server, originalDataManager.getDataFixer()); - WorldData newWorldData = new WorldData(originalWorld.worldData.a((NBTTagCompound) null), - server.dataConverterManager, getDataVersion(), null); - newWorldData.setName(UUID.randomUUID().toString()); - - ChunkGenerator gen = world.getGenerator(); - Environment env = world.getEnvironment(); - try (WorldServer freshWorld = new WorldServer(server, - server.executorService, saveHandler, - newWorldData, - originalWorld.worldProvider.getDimensionManager(), - originalWorld.getMethodProfiler(), - server.worldLoadListenerFactory.create(11), - env, - gen) { - @Override - public boolean addEntityChunk(net.minecraft.server.v1_15_R1.Entity entity) { - //Fixes #320; Prevent adding entities so we aren't attempting to spawn them asynchronously - return false; - } - }) { - - // Pre-gen all the chunks - // We need to also pull one more chunk in every direction - Fawe.get().getQueueHandler().startSet(true); - try { - IQueueExtent extent = new SingleThreadQueueExtent(); - extent.init(null, (x, z) -> new BukkitGetBlocks_1_15_2(freshWorld, x, z) { - @Override - public Chunk ensureLoaded(World nmsWorld, int chunkX, int chunkZ) { - Chunk cached = nmsWorld.getChunkIfLoaded(chunkX, chunkZ); - if (cached != null) { - return cached; - } - Future future = Fawe.get().getQueueHandler().sync((Supplier) () -> freshWorld.getChunkAt(chunkX, chunkZ)); - while (!future.isDone()) { - // this feels so dirty - freshWorld.getChunkProvider().runTasks(); - } - try { - return future.get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - } - }, null); - for (BlockVector3 vec : region) { - editSession.setBlock(vec, extent.getFullBlock(vec)); - } - } finally { - Fawe.get().getQueueHandler().endSet(true); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } catch (MaxChangedBlocksException e) { - throw new RuntimeException(e); - } finally { - saveFolder.delete(); - } - return true; + public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception { + return new Regen_v1_15_R2(bukkitWorld, region, target, options).regenerate(); } @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java index 44e42d099..cda1690cd 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java @@ -28,23 +28,26 @@ import com.boydti.fawe.bukkit.adapter.mc1_16_1.BukkitGetBlocks_1_16_1; import com.boydti.fawe.bukkit.adapter.mc1_16_1.FAWEWorldNativeAccess_1_16; import com.boydti.fawe.bukkit.adapter.mc1_16_1.MapChunkUtil_1_16_1; import com.boydti.fawe.bukkit.adapter.mc1_16_1.nbt.LazyCompoundTag_1_16_1; +import com.google.common.base.Preconditions; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.TileEntityBlock; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.regen.Regen_v1_16_R1; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.LazyBaseEntity; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.wna.WorldNativeAccess; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; @@ -101,7 +104,6 @@ import static org.slf4j.LoggerFactory.getLogger; public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter { private final Spigot_v1_16_R1 parent; private char[] ibdToStateOrdinal; - // ------------------------------------------------------------------------ // Code that may break between versions of Minecraft // ------------------------------------------------------------------------ @@ -139,8 +141,6 @@ public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements I public BlockMaterial getMaterial(BlockState state) { IBlockData bs = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState(); return new BlockMaterial_1_16_1(bs.getBlock(), bs); - - } public Block getBlock(BlockType blockType) { @@ -150,7 +150,7 @@ public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements I @SuppressWarnings("deprecation") @Override public BaseBlock getBlock(Location location) { - checkNotNull(location); + Preconditions.checkNotNull(location); CraftWorld craftWorld = ((CraftWorld) location.getWorld()); int x = location.getBlockX(); @@ -254,7 +254,7 @@ public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements I @Override public BaseEntity getEntity(org.bukkit.entity.Entity entity) { - checkNotNull(entity); + Preconditions.checkNotNull(entity); CraftEntity craftEntity = ((CraftEntity) entity); Entity mcEntity = craftEntity.getHandle(); @@ -401,81 +401,8 @@ public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements I } @Override - public boolean regenerate(org.bukkit.World world, Region region, EditSession editSession) { -// WorldServer originalWorld = ((CraftWorld) world).getHandle(); -// ChunkProviderServer provider = originalWorld.getChunkProvider(); -// if (!(provider instanceof ChunkProviderServer)) { -// return false; -// } -// -// File saveFolder = Files.createTempDir(); -// // register this just in case something goes wrong -// // normally it should be deleted at the end of this method -// saveFolder.deleteOnExit(); -// try { -// MinecraftServer server = originalWorld.getServer().getServer(); -// Convertable.ConversionSession originalDataManager = server.convertable; -//// Convertable.ConversionSession saveHandler = new Convertable.ConversionSession(world.getName(), world.); -// WorldData newWorldData = new WorldData(originalWorld.worldData.a((NBTTagCompound) null), -// server.dataConverterManager, getDataVersion(), null); -// newWorldData.setName(UUID.randomUUID().toString()); -// -// ChunkGenerator gen = world.getGenerator(); -// Environment env = world.getEnvironment(); -// try (WorldServer freshWorld = new WorldServer(server, -// server.executorService, originalDataManager, -// newWorldData, -// originalWorld.worldProvider.getDimensionManager(), -// originalWorld.getMethodProfiler(), -// server.worldLoadListenerFactory.create(11), -// env, -// gen) { -// @Override -// public boolean addEntityChunk(Entity entity) { -// //Fixes #320; Prevent adding entities so we aren't attempting to spawn them asynchronously -// return false; -// } -// }) { -// -// // Pre-gen all the chunks -// // We need to also pull one more chunk in every direction -// Fawe.get().getQueueHandler().startSet(true); -// try { -// IQueueExtent extent = new SingleThreadQueueExtent(); -// extent.init(null, (x, z) -> new BukkitGetBlocks_1_16_1(freshWorld, x, z) { -// @Override -// public Chunk ensureLoaded(World nmsWorld, int X, int Z) { -// Chunk cached = nmsWorld.getChunkIfLoaded(X, Z); -// if (cached != null) return cached; -// Future future = Fawe.get().getQueueHandler().sync((Supplier) () -> freshWorld.getChunkAt(X, Z)); -// while (!future.isDone()) { -// // this feels so dirty -// freshWorld.getChunkProvider().runTasks(); -// } -// try { -// return future.get(); -// } catch (InterruptedException | ExecutionException e) { -// throw new RuntimeException(e); -// } -// } -// }, null); -// for (BlockVector3 vec : region) { -// editSession.setBlock(vec, extent.getFullBlock(vec)); -// } -// } finally { -// Fawe.get().getQueueHandler().endSet(true); -// } -// } catch (IOException e) { -// throw new RuntimeException(e); -// } -// } catch (MaxChangedBlocksException e) { -// throw new RuntimeException(e); -// } finally { -// saveFolder.delete(); -// } -// return true; - - return false; //TODO: rework or remove for 1.16 + public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception { + return new Regen_v1_16_R1(bukkitWorld, region, target, options).regenerate(); } @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R2.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R2.java index 303c405d7..056988439 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R2.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R2.java @@ -28,23 +28,26 @@ import com.boydti.fawe.bukkit.adapter.mc1_16_2.BukkitGetBlocks_1_16_2; import com.boydti.fawe.bukkit.adapter.mc1_16_2.FAWEWorldNativeAccess_1_16; import com.boydti.fawe.bukkit.adapter.mc1_16_2.MapChunkUtil_1_16_2; import com.boydti.fawe.bukkit.adapter.mc1_16_2.nbt.LazyCompoundTag_1_16_2; +import com.google.common.base.Preconditions; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.TileEntityBlock; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.regen.Regen_v1_16_R2; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.LazyBaseEntity; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.wna.WorldNativeAccess; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; @@ -87,7 +90,6 @@ import org.bukkit.craftbukkit.v1_16_R2.entity.CraftEntity; import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; import org.bukkit.craftbukkit.v1_16_R2.inventory.CraftItemStack; import org.bukkit.entity.Player; - import java.lang.ref.WeakReference; import java.util.Map; import java.util.OptionalInt; @@ -149,7 +151,7 @@ public final class FAWE_Spigot_v1_16_R2 extends CachedBukkitAdapter implements I @SuppressWarnings("deprecation") @Override public BaseBlock getBlock(Location location) { - checkNotNull(location); + Preconditions.checkNotNull(location); CraftWorld craftWorld = ((CraftWorld) location.getWorld()); int x = location.getBlockX(); @@ -253,7 +255,7 @@ public final class FAWE_Spigot_v1_16_R2 extends CachedBukkitAdapter implements I @Override public BaseEntity getEntity(org.bukkit.entity.Entity entity) { - checkNotNull(entity); + Preconditions.checkNotNull(entity); CraftEntity craftEntity = ((CraftEntity) entity); Entity mcEntity = craftEntity.getHandle(); @@ -400,81 +402,8 @@ public final class FAWE_Spigot_v1_16_R2 extends CachedBukkitAdapter implements I } @Override - public boolean regenerate(org.bukkit.World world, Region region, EditSession editSession) { -// WorldServer originalWorld = ((CraftWorld) world).getHandle(); -// ChunkProviderServer provider = originalWorld.getChunkProvider(); -// if (!(provider instanceof ChunkProviderServer)) { -// return false; -// } -// -// File saveFolder = Files.createTempDir(); -// // register this just in case something goes wrong -// // normally it should be deleted at the end of this method -// saveFolder.deleteOnExit(); -// try { -// MinecraftServer server = originalWorld.getServer().getServer(); -// Convertable.ConversionSession originalDataManager = server.convertable; -//// Convertable.ConversionSession saveHandler = new Convertable.ConversionSession(world.getName(), world.); -// WorldData newWorldData = new WorldData(originalWorld.worldData.a((NBTTagCompound) null), -// server.dataConverterManager, getDataVersion(), null); -// newWorldData.setName(UUID.randomUUID().toString()); -// -// ChunkGenerator gen = world.getGenerator(); -// Environment env = world.getEnvironment(); -// try (WorldServer freshWorld = new WorldServer(server, -// server.executorService, originalDataManager, -// newWorldData, -// originalWorld.worldProvider.getDimensionManager(), -// originalWorld.getMethodProfiler(), -// server.worldLoadListenerFactory.create(11), -// env, -// gen) { -// @Override -// public boolean addEntityChunk(Entity entity) { -// //Fixes #320; Prevent adding entities so we aren't attempting to spawn them asynchronously -// return false; -// } -// }) { -// -// // Pre-gen all the chunks -// // We need to also pull one more chunk in every direction -// Fawe.get().getQueueHandler().startSet(true); -// try { -// IQueueExtent extent = new SingleThreadQueueExtent(); -// extent.init(null, (x, z) -> new BukkitGetBlocks_1_16_2(freshWorld, x, z) { -// @Override -// public Chunk ensureLoaded(World nmsWorld, int X, int Z) { -// Chunk cached = nmsWorld.getChunkIfLoaded(X, Z); -// if (cached != null) return cached; -// Future future = Fawe.get().getQueueHandler().sync((Supplier) () -> freshWorld.getChunkAt(X, Z)); -// while (!future.isDone()) { -// // this feels so dirty -// freshWorld.getChunkProvider().runTasks(); -// } -// try { -// return future.get(); -// } catch (InterruptedException | ExecutionException e) { -// throw new RuntimeException(e); -// } -// } -// }, null); -// for (BlockVector3 vec : region) { -// editSession.setBlock(vec, extent.getFullBlock(vec)); -// } -// } finally { -// Fawe.get().getQueueHandler().endSet(true); -// } -// } catch (IOException e) { -// throw new RuntimeException(e); -// } -// } catch (MaxChangedBlocksException e) { -// throw new RuntimeException(e); -// } finally { -// saveFolder.delete(); -// } -// return true; - - return false; //TODO: rework or remove for 1.16 + public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception { + return new Regen_v1_16_R2(bukkitWorld, region, target, options).regenerate(); } @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_15_R2.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_15_R2.java new file mode 100644 index 000000000..3f9958194 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_15_R2.java @@ -0,0 +1,478 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.regen; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.beta.IChunkCache; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.bukkit.adapter.mc1_15_2.BukkitGetBlocks_1_15_2; +import com.mojang.datafixers.util.Either; +import com.sk89q.worldedit.bukkit.adapter.Regenerator; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.io.file.SafeFiles; +import com.sk89q.worldedit.world.RegenOptions; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BooleanSupplier; +import java.util.function.LongFunction; +import java.util.function.Supplier; +import javax.annotation.Nullable; +import net.minecraft.server.v1_15_R1.Area; +import net.minecraft.server.v1_15_R1.AreaContextTransformed; +import net.minecraft.server.v1_15_R1.AreaFactory; +import net.minecraft.server.v1_15_R1.AreaTransformer8; +import net.minecraft.server.v1_15_R1.BiomeBase; +import net.minecraft.server.v1_15_R1.BiomeLayoutOverworldConfiguration; +import net.minecraft.server.v1_15_R1.Biomes; +import net.minecraft.server.v1_15_R1.Chunk; +import net.minecraft.server.v1_15_R1.ChunkConverter; +import net.minecraft.server.v1_15_R1.ChunkCoordIntPair; +import net.minecraft.server.v1_15_R1.ChunkGenerator; +import net.minecraft.server.v1_15_R1.ChunkProviderFlat; +import net.minecraft.server.v1_15_R1.ChunkProviderGenerate; +import net.minecraft.server.v1_15_R1.ChunkProviderHell; +import net.minecraft.server.v1_15_R1.ChunkProviderServer; +import net.minecraft.server.v1_15_R1.ChunkProviderTheEnd; +import net.minecraft.server.v1_15_R1.ChunkStatus; +import net.minecraft.server.v1_15_R1.DefinedStructureManager; +import net.minecraft.server.v1_15_R1.GenLayer; +import net.minecraft.server.v1_15_R1.GenLayers; +import net.minecraft.server.v1_15_R1.GeneratorSettingsEnd; +import net.minecraft.server.v1_15_R1.GeneratorSettingsFlat; +import net.minecraft.server.v1_15_R1.GeneratorSettingsNether; +import net.minecraft.server.v1_15_R1.GeneratorSettingsOverworld; +import net.minecraft.server.v1_15_R1.IChunkAccess; +import net.minecraft.server.v1_15_R1.IRegistry; +import net.minecraft.server.v1_15_R1.LightEngineThreaded; +import net.minecraft.server.v1_15_R1.LinearCongruentialGenerator; +import net.minecraft.server.v1_15_R1.MinecraftServer; +import net.minecraft.server.v1_15_R1.NBTTagCompound; +import net.minecraft.server.v1_15_R1.NoiseGeneratorPerlin; +import net.minecraft.server.v1_15_R1.ProtoChunk; +import net.minecraft.server.v1_15_R1.World; +import net.minecraft.server.v1_15_R1.WorldChunkManager; +import net.minecraft.server.v1_15_R1.WorldChunkManagerOverworld; +import net.minecraft.server.v1_15_R1.WorldData; +import net.minecraft.server.v1_15_R1.WorldLoadListener; +import net.minecraft.server.v1_15_R1.WorldNBTStorage; +import net.minecraft.server.v1_15_R1.WorldServer; +import net.minecraft.server.v1_15_R1.WorldType; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_15_R1.CraftServer; +import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_15_R1.generator.CustomChunkGenerator; +import org.bukkit.craftbukkit.v1_15_R1.util.CraftMagicNumbers; +import org.bukkit.generator.BlockPopulator; + +public class Regen_v1_15_R2 extends Regenerator { + + private static final Field serverWorldsField; + private static final Field worldPaperConfigField; + private static final Field flatBedrockField; + private static final Field delegateField; + private static final Field chunkProviderField; + + //list of chunk stati in correct order without FULL + private static final Map chunkStati = new LinkedHashMap<>(); + + static { + chunkStati.put(ChunkStatus.EMPTY, Regenerator.Concurrency.FULL); // radius -1, does nothing + chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Regenerator.Concurrency.NONE); // uses unsynchronized maps + chunkStati.put(ChunkStatus.STRUCTURE_REFERENCES, Regenerator.Concurrency.FULL); // radius 8, but no writes to other chunks, only current chunk + chunkStati.put(ChunkStatus.BIOMES, Regenerator.Concurrency.FULL); // radius 0 + chunkStati.put(ChunkStatus.NOISE, Regenerator.Concurrency.RADIUS); // radius 8 + chunkStati.put(ChunkStatus.SURFACE, Regenerator.Concurrency.FULL); // radius 0 + chunkStati.put(ChunkStatus.CARVERS, Regenerator.Concurrency.NONE); // radius 0, but RADIUS and FULL change results + chunkStati.put(ChunkStatus.LIQUID_CARVERS, Regenerator.Concurrency.NONE); // radius 0, but RADIUS and FULL change results + chunkStati.put(ChunkStatus.FEATURES, Regenerator.Concurrency.NONE); // uses unsynchronized maps + chunkStati.put(ChunkStatus.LIGHT, Regenerator.Concurrency.FULL); // radius 1, but no writes to other chunks, only current chunk + chunkStati.put(ChunkStatus.SPAWN, Regenerator.Concurrency.FULL); // radius 0 + chunkStati.put(ChunkStatus.HEIGHTMAPS, Regenerator.Concurrency.FULL); // radius 0 + + try { + serverWorldsField = CraftServer.class.getDeclaredField("worlds"); + serverWorldsField.setAccessible(true); + + Field tmpPaperConfigField = null; + Field tmpFlatBedrockField = null; + try { //only present on paper + tmpPaperConfigField = World.class.getDeclaredField("paperConfig"); + tmpPaperConfigField.setAccessible(true); + + tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock"); + tmpFlatBedrockField.setAccessible(true); + } catch (Exception e) { + tmpPaperConfigField = null; + tmpFlatBedrockField = null; + } + worldPaperConfigField = tmpPaperConfigField; + flatBedrockField = tmpFlatBedrockField; + + delegateField = CustomChunkGenerator.class.getDeclaredField("delegate"); + delegateField.setAccessible(true); + + chunkProviderField = World.class.getDeclaredField("chunkProvider"); + chunkProviderField.setAccessible(true); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + //runtime + private WorldServer originalNMSWorld; + private ChunkProviderServer originalChunkProvider; + private WorldServer freshNMSWorld; + private ChunkProviderServer freshChunkProvider; + private DefinedStructureManager structureManager; + private LightEngineThreaded lightEngine; + private ChunkGenerator generator; + + private Path tempDir; + + private boolean generateFlatBedrock = false; + + public Regen_v1_15_R2(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) { + super(originalBukkitWorld, region, target, options); + } + + @Override + protected boolean prepare() { + this.originalNMSWorld = ((CraftWorld) originalBukkitWorld).getHandle(); + originalChunkProvider = originalNMSWorld.getChunkProvider(); + if (!(originalChunkProvider instanceof ChunkProviderServer)) { + return false; + } + + //flat bedrock? (only on paper) + try { + generateFlatBedrock = flatBedrockField.getBoolean(worldPaperConfigField.get(originalNMSWorld)); + } catch (Exception ignored) { + } + + seed = options.getSeed().orElse(originalNMSWorld.getSeed()); + chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c)); + + return true; + } + + @Override + protected boolean initNewWorld() throws Exception { + //world folder + tempDir = java.nio.file.Files.createTempDirectory("WorldEditWorldGen"); + + //prepare for world init (see upstream implementation for reference) + org.bukkit.World.Environment env = originalBukkitWorld.getEnvironment(); + org.bukkit.generator.ChunkGenerator gen = originalBukkitWorld.getGenerator(); + + MinecraftServer server = originalNMSWorld.getServer().getServer(); + WorldData newWorldData = new WorldData(originalNMSWorld.worldData.a((NBTTagCompound) null), server.dataConverterManager, CraftMagicNumbers.INSTANCE.getDataVersion(), (NBTTagCompound) null); + newWorldData.setName("worldeditregentempworld"); + WorldNBTStorage saveHandler = new WorldNBTStorage(new File(tempDir.toUri()), originalNMSWorld.getDataManager().getDirectory().getName(), server, server.dataConverterManager); + + //init world + freshNMSWorld = Fawe.get().getQueueHandler().sync((Supplier) () -> new WorldServer(server, server.executorService, saveHandler, newWorldData, originalNMSWorld.worldProvider.getDimensionManager(), originalNMSWorld.getMethodProfiler(), new RegenNoOpWorldLoadListener(), env, gen) { + @Override + public void doTick(BooleanSupplier booleansupplier) { //no ticking + } + }).get(); + freshNMSWorld.savingDisabled = true; + removeWorldFromWorldsMap(); + newWorldData.checkName(originalNMSWorld.getWorldData().getName()); //rename to original world name + + try { //flat bedrock (paper only) + Object paperconf = worldPaperConfigField.get(freshNMSWorld); + flatBedrockField.setBoolean(paperconf, generateFlatBedrock); + } catch (Exception e) { + } + + DefinedStructureManager tmpStructureManager = saveHandler.f(); + freshChunkProvider = new ChunkProviderServer(freshNMSWorld, saveHandler.getDirectory(), server.aC(), tmpStructureManager, server.executorService, originalChunkProvider.chunkGenerator, freshNMSWorld.spigotConfig.viewDistance, new RegenNoOpWorldLoadListener(), () -> freshNMSWorld.getWorldPersistentData()) { + // redirect to our protoChunks list + @Override + public IChunkAccess getChunkAt(int x, int z, ChunkStatus chunkstatus, boolean flag) { + return getProtoChunkAt(x, z); + } + }; + chunkProviderField.set(freshNMSWorld, freshChunkProvider); + + //generator + if (originalChunkProvider.getChunkGenerator() instanceof ChunkProviderFlat) { + GeneratorSettingsFlat generatorSettingFlat = (GeneratorSettingsFlat) originalChunkProvider.getChunkGenerator().getSettings(); + generator = new ChunkProviderFlat(freshNMSWorld, originalChunkProvider.getChunkGenerator().getWorldChunkManager(), generatorSettingFlat); + } else if (originalChunkProvider.getChunkGenerator() instanceof ChunkProviderGenerate) { //overworld + GeneratorSettingsOverworld settings = (GeneratorSettingsOverworld) originalChunkProvider.getChunkGenerator().getSettings(); + WorldChunkManager chunkManager = originalChunkProvider.getChunkGenerator().getWorldChunkManager(); + if (chunkManager instanceof WorldChunkManagerOverworld) { //should always be true + chunkManager = fastOverWorldChunkManager(chunkManager); + } + generator = new ChunkProviderGenerate(freshNMSWorld, chunkManager, settings); + } else if (originalChunkProvider.getChunkGenerator() instanceof ChunkProviderHell) { //nether + GeneratorSettingsNether settings = (GeneratorSettingsNether) originalChunkProvider.getChunkGenerator().getSettings(); + generator = new ChunkProviderHell(freshNMSWorld, originalChunkProvider.getChunkGenerator().getWorldChunkManager(), settings); + } else if (originalChunkProvider.getChunkGenerator() instanceof ChunkProviderTheEnd) { //end + GeneratorSettingsEnd settings = (GeneratorSettingsEnd) originalChunkProvider.getChunkGenerator().getSettings(); + generator = new ChunkProviderTheEnd(freshNMSWorld, originalChunkProvider.getChunkGenerator().getWorldChunkManager(), settings); + } else if (originalChunkProvider.getChunkGenerator() instanceof CustomChunkGenerator) { + ChunkGenerator delegate = (ChunkGenerator) delegateField.get(originalChunkProvider.getChunkGenerator()); + generator = delegate; + } else { + System.out.println("Unsupported generator type " + originalChunkProvider.getChunkGenerator().getClass().getName()); + return false; + } + if (originalNMSWorld.generator != null) { + // wrap custom world generator + generator = new CustomChunkGenerator(freshNMSWorld, originalNMSWorld.generator); + generateConcurrent = originalNMSWorld.generator.isParallelCapable(); + } + + //lets start then + structureManager = tmpStructureManager; + lightEngine = freshChunkProvider.getLightEngine(); + + return true; + } + + @Override + protected void cleanup() { + //shutdown chunk provider + try { + Fawe.get().getQueueHandler().sync(() -> { + try { + freshChunkProvider.close(false); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } catch (Exception e) { + } + + //remove world from server + try { + removeWorldFromWorldsMap(); + } catch (Exception e) { + } + + //delete directory + try { + SafeFiles.tryHardToDeleteDir(tempDir); + } catch (Exception e) { + } + } + + @Override + protected ProtoChunk createProtoChunk(int x, int z) { + return new ProtoChunk(new ChunkCoordIntPair(x, z), ChunkConverter.a) { + public boolean generateFlatBedrock() { + return generateFlatBedrock; + } + }; + } + + @Override + protected Chunk createChunk(ProtoChunk protoChunk) { + return new Chunk(freshNMSWorld, protoChunk); + } + + @Override + protected ChunkStatusWrap getFullChunkStatus() { + return new ChunkStatusWrap(ChunkStatus.FULL); + } + + @Override + protected List getBlockPopulators() { + return originalNMSWorld.getWorld().getPopulators(); + } + + @Override + protected void populate(Chunk chunk, Random random, BlockPopulator pop) { + pop.populate(freshNMSWorld.getWorld(), random, chunk.bukkitChunk); + } + + @Override + protected IChunkCache initSourceQueueCache() { + return (chunkX, chunkZ) -> new BukkitGetBlocks_1_15_2(freshNMSWorld, chunkX, chunkZ) { + @Override + public Chunk ensureLoaded(World nmsWorld, int x, int z) { + return getChunkAt(x, z); + } + }; + } + + protected class ChunkStatusWrap extends Regenerator.ChunkStatusWrapper { + + private final ChunkStatus chunkStatus; + + public ChunkStatusWrap(ChunkStatus chunkStatus) { + this.chunkStatus = chunkStatus; + } + + @Override + public int requiredNeigborChunkRadius() { + return chunkStatus.f(); + } + + @Override + public String name() { + return chunkStatus.d(); + } + + @Override + public void processChunk(Long xz, List accessibleChunks) { + chunkStatus.a(freshNMSWorld, + generator, + structureManager, + lightEngine, + c -> CompletableFuture.completedFuture(Either.left(c)), + accessibleChunks); + } + } + + //util + private void removeWorldFromWorldsMap() { + Fawe.get().getQueueHandler().sync(() -> { + try { + Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + map.remove("worldeditregentempworld"); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + } + + private WorldChunkManager fastOverWorldChunkManager(WorldChunkManager chunkManager) throws Exception { + Field genLayerField = WorldChunkManagerOverworld.class.getDeclaredField("d"); + genLayerField.setAccessible(true); + Field areaLazyField = GenLayer.class.getDeclaredField("b"); + areaLazyField.setAccessible(true); + Method initAreaFactoryMethod = GenLayers.class.getDeclaredMethod("a", WorldType.class, GeneratorSettingsOverworld.class, LongFunction.class); + initAreaFactoryMethod.setAccessible(true); + + //init new WorldChunkManagerOverworld + BiomeLayoutOverworldConfiguration biomeconfig = new BiomeLayoutOverworldConfiguration(freshNMSWorld.getWorldData()) + .a((GeneratorSettingsOverworld) originalChunkProvider.getChunkGenerator().getSettings()); + chunkManager = new WorldChunkManagerOverworld(biomeconfig); + + //replace genLayer + AreaFactory factory = (AreaFactory) initAreaFactoryMethod.invoke(null, biomeconfig.b(), biomeconfig.c(), (LongFunction) (l -> new FastWorldGenContextArea(seed, l))); + genLayerField.set(chunkManager, new FastGenLayer(factory)); + + return chunkManager; + } + + private static class FastWorldGenContextArea implements AreaContextTransformed { + + private final ConcurrentHashMap sharedAreaMap = new ConcurrentHashMap<>(); + private final NoiseGeneratorPerlin perlinNoise; + private final long magicrandom; + private final ConcurrentHashMap map = new ConcurrentHashMap<>(); //needed for multithreaded generation + + public FastWorldGenContextArea(long seed, long lconst) { + this.magicrandom = mix(seed, lconst); + this.perlinNoise = new NoiseGeneratorPerlin(new Random(seed)); + } + + @Override + public FastAreaLazy a(AreaTransformer8 var0) { + return new FastAreaLazy(sharedAreaMap, var0); + } + + @Override + public void a(long x, long z) { + long l = this.magicrandom; + l = LinearCongruentialGenerator.a(l, x); + l = LinearCongruentialGenerator.a(l, z); + l = LinearCongruentialGenerator.a(l, x); + l = LinearCongruentialGenerator.a(l, z); + this.map.put(Thread.currentThread().getId(), l); + } + + @Override + public int a(int y) { + long tid = Thread.currentThread().getId(); + long e = this.map.computeIfAbsent(tid, i -> 0L); + int mod = (int) Math.floorMod(e >> 24L, (long) y); + this.map.put(tid, LinearCongruentialGenerator.a(e, this.magicrandom)); + return mod; + } + + @Override + public NoiseGeneratorPerlin b() { + return this.perlinNoise; + } + + private static long mix(long seed, long lconst) { + long l1 = lconst; + l1 = LinearCongruentialGenerator.a(l1, lconst); + l1 = LinearCongruentialGenerator.a(l1, lconst); + l1 = LinearCongruentialGenerator.a(l1, lconst); + long l2 = seed; + l2 = LinearCongruentialGenerator.a(l2, l1); + l2 = LinearCongruentialGenerator.a(l2, l1); + l2 = LinearCongruentialGenerator.a(l2, l1); + return l2; + } + } + + private static class FastGenLayer extends GenLayer { + + private final FastAreaLazy areaLazy; + + public FastGenLayer(AreaFactory factory) throws Exception { + super(() -> null); + this.areaLazy = factory.make(); + } + + @Override + public BiomeBase a(int x, int z) { + BiomeBase biome = IRegistry.BIOME.fromId(this.areaLazy.a(x, z)); + if (biome == null) + return Biomes.b; + return biome; + } + } + + private static class FastAreaLazy implements Area { + + private final AreaTransformer8 transformer; + //ConcurrentHashMap is 50% faster that Long2IntLinkedOpenHashMap in a syncronized context + //using a map for each thread worsens the performance significantly due to cache misses (factor 5) + private final ConcurrentHashMap sharedMap; + + public FastAreaLazy(ConcurrentHashMap sharedMap, AreaTransformer8 transformer) { + this.sharedMap = sharedMap; + this.transformer = transformer; + } + + @Override + public int a(int x, int z) { + long zx = ChunkCoordIntPair.pair(x, z); + return this.sharedMap.computeIfAbsent(zx, i -> this.transformer.apply(x, z)); + } + } + + private static class RegenNoOpWorldLoadListener implements WorldLoadListener { + + private RegenNoOpWorldLoadListener() { + } + + @Override + public void a(ChunkCoordIntPair chunkCoordIntPair) { + } + + @Override + public void a(ChunkCoordIntPair chunkCoordIntPair, @Nullable ChunkStatus chunkStatus) { + } + + @Override + public void b() { + } + } +} diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_16_R1.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_16_R1.java new file mode 100644 index 000000000..af66ef955 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_16_R1.java @@ -0,0 +1,519 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.regen; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.beta.IChunkCache; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.bukkit.adapter.mc1_16_1.BukkitGetBlocks_1_16_1; +import com.google.common.collect.ImmutableList; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Dynamic; +import com.mojang.serialization.Lifecycle; +import com.sk89q.worldedit.bukkit.adapter.Regenerator; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.io.file.SafeFiles; +import com.sk89q.worldedit.world.RegenOptions; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BooleanSupplier; +import java.util.function.LongFunction; +import java.util.function.Supplier; +import javax.annotation.Nullable; +import net.minecraft.server.v1_16_R1.Area; +import net.minecraft.server.v1_16_R1.AreaContextTransformed; +import net.minecraft.server.v1_16_R1.AreaFactory; +import net.minecraft.server.v1_16_R1.AreaTransformer8; +import net.minecraft.server.v1_16_R1.BiomeBase; +import net.minecraft.server.v1_16_R1.Biomes; +import net.minecraft.server.v1_16_R1.Chunk; +import net.minecraft.server.v1_16_R1.ChunkConverter; +import net.minecraft.server.v1_16_R1.ChunkCoordIntPair; +import net.minecraft.server.v1_16_R1.ChunkGenerator; +import net.minecraft.server.v1_16_R1.ChunkGeneratorAbstract; +import net.minecraft.server.v1_16_R1.ChunkProviderFlat; +import net.minecraft.server.v1_16_R1.ChunkProviderServer; +import net.minecraft.server.v1_16_R1.ChunkStatus; +import net.minecraft.server.v1_16_R1.Convertable; +import net.minecraft.server.v1_16_R1.DefinedStructureManager; +import net.minecraft.server.v1_16_R1.DynamicOpsNBT; +import net.minecraft.server.v1_16_R1.GenLayer; +import net.minecraft.server.v1_16_R1.GenLayers; +import net.minecraft.server.v1_16_R1.GeneratorSettingBase; +import net.minecraft.server.v1_16_R1.GeneratorSettings; +import net.minecraft.server.v1_16_R1.GeneratorSettingsFlat; +import net.minecraft.server.v1_16_R1.IChunkAccess; +import net.minecraft.server.v1_16_R1.IRegistry; +import net.minecraft.server.v1_16_R1.IRegistryCustom; +import net.minecraft.server.v1_16_R1.LightEngineThreaded; +import net.minecraft.server.v1_16_R1.LinearCongruentialGenerator; +import net.minecraft.server.v1_16_R1.MinecraftServer; +import net.minecraft.server.v1_16_R1.NBTBase; +import net.minecraft.server.v1_16_R1.NBTTagCompound; +import net.minecraft.server.v1_16_R1.NoiseGeneratorPerlin; +import net.minecraft.server.v1_16_R1.ProtoChunk; +import net.minecraft.server.v1_16_R1.RegistryReadOps; +import net.minecraft.server.v1_16_R1.ResourceKey; +import net.minecraft.server.v1_16_R1.World; +import net.minecraft.server.v1_16_R1.WorldChunkManager; +import net.minecraft.server.v1_16_R1.WorldChunkManagerOverworld; +import net.minecraft.server.v1_16_R1.WorldDataServer; +import net.minecraft.server.v1_16_R1.WorldDimension; +import net.minecraft.server.v1_16_R1.WorldLoadListener; +import net.minecraft.server.v1_16_R1.WorldServer; +import net.minecraft.server.v1_16_R1.WorldSettings; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_16_R1.CraftServer; +import org.bukkit.craftbukkit.v1_16_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R1.generator.CustomChunkGenerator; +import org.bukkit.generator.BlockPopulator; + +public class Regen_v1_16_R1 extends Regenerator { + + private static final Field serverWorldsField; + private static final Field worldPaperConfigField; + private static final Field flatBedrockField; + private static final Field generatorSettingBaseField; + private static final Field generatorSettingFlatField; + private static final Field delegateField; + private static final Field chunkProviderField; + + //list of chunk stati in correct order without FULL + private static final Map chunkStati = new LinkedHashMap<>(); + + static { + chunkStati.put(ChunkStatus.EMPTY, Regenerator.Concurrency.FULL); // radius -1, does nothing + chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Regenerator.Concurrency.NONE); // uses unsynchronized maps + chunkStati.put(ChunkStatus.STRUCTURE_REFERENCES, Regenerator.Concurrency.FULL); // radius 8, but no writes to other chunks, only current chunk + chunkStati.put(ChunkStatus.BIOMES, Regenerator.Concurrency.FULL); // radius 0 + chunkStati.put(ChunkStatus.NOISE, Regenerator.Concurrency.RADIUS); // radius 8 + chunkStati.put(ChunkStatus.SURFACE, Regenerator.Concurrency.FULL); // radius 0 + chunkStati.put(ChunkStatus.CARVERS, Regenerator.Concurrency.NONE); // radius 0, but RADIUS and FULL change results + chunkStati.put(ChunkStatus.LIQUID_CARVERS, Regenerator.Concurrency.NONE); // radius 0, but RADIUS and FULL change results + chunkStati.put(ChunkStatus.FEATURES, Regenerator.Concurrency.NONE); // uses unsynchronized maps + chunkStati.put(ChunkStatus.LIGHT, Regenerator.Concurrency.FULL); // radius 1, but no writes to other chunks, only current chunk + chunkStati.put(ChunkStatus.SPAWN, Regenerator.Concurrency.FULL); // radius 0 + chunkStati.put(ChunkStatus.HEIGHTMAPS, Regenerator.Concurrency.FULL); // radius 0 + + try { + serverWorldsField = CraftServer.class.getDeclaredField("worlds"); + serverWorldsField.setAccessible(true); + + Field tmpPaperConfigField = null; + Field tmpFlatBedrockField = null; + try { //only present on paper + tmpPaperConfigField = World.class.getDeclaredField("paperConfig"); + tmpPaperConfigField.setAccessible(true); + + tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock"); + tmpFlatBedrockField.setAccessible(true); + } catch (Exception e) { + tmpPaperConfigField = null; + tmpFlatBedrockField = null; + } + worldPaperConfigField = tmpPaperConfigField; + flatBedrockField = tmpFlatBedrockField; + + generatorSettingBaseField = ChunkGeneratorAbstract.class.getDeclaredField("h"); + generatorSettingBaseField.setAccessible(true); + + generatorSettingFlatField = ChunkProviderFlat.class.getDeclaredField("e"); + generatorSettingFlatField.setAccessible(true); + + delegateField = CustomChunkGenerator.class.getDeclaredField("delegate"); + delegateField.setAccessible(true); + + chunkProviderField = WorldServer.class.getDeclaredField("chunkProvider"); + chunkProviderField.setAccessible(true); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + //runtime + private WorldServer originalNMSWorld; + private ChunkProviderServer originalChunkProvider; + private WorldServer freshNMSWorld; + private ChunkProviderServer freshChunkProvider; + private Convertable.ConversionSession session; + private DefinedStructureManager structureManager; + private LightEngineThreaded lightEngine; + private ChunkGenerator generator; + + private Path tempDir; + + private boolean generateFlatBedrock = false; + + public Regen_v1_16_R1(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) { + super(originalBukkitWorld, region, target, options); + } + + @Override + protected boolean prepare() { + this.originalNMSWorld = ((CraftWorld) originalBukkitWorld).getHandle(); + originalChunkProvider = originalNMSWorld.getChunkProvider(); + if (!(originalChunkProvider instanceof ChunkProviderServer)) { + return false; + } + + //flat bedrock? (only on paper) + try { + generateFlatBedrock = flatBedrockField.getBoolean(worldPaperConfigField.get(originalNMSWorld)); + } catch (Exception ignored) { + } + + seed = options.getSeed().orElse(originalNMSWorld.getSeed()); + chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c)); + + return true; + } + + @Override + protected boolean initNewWorld() throws Exception { + //world folder + tempDir = java.nio.file.Files.createTempDirectory("WorldEditWorldGen"); + + //prepare for world init (see upstream implementation for reference) + org.bukkit.World.Environment env = originalBukkitWorld.getEnvironment(); + org.bukkit.generator.ChunkGenerator gen = originalBukkitWorld.getGenerator(); + Convertable convertable = Convertable.a(tempDir); + ResourceKey worldDimKey = getWorldDimKey(env); + session = convertable.c("worldeditregentempworld", worldDimKey); + WorldDataServer originalWorldData = originalNMSWorld.worldDataServer; + + MinecraftServer server = originalNMSWorld.getServer().getServer(); + WorldDataServer levelProperties = (WorldDataServer) server.getSaveData(); + GeneratorSettings newOpts = GeneratorSettings.a.encodeStart(DynamicOpsNBT.a, levelProperties.getGeneratorSettings()).flatMap(tag -> GeneratorSettings.a.parse(this.recursivelySetSeed(new Dynamic<>(DynamicOpsNBT.a, tag), seed, new HashSet<>()))).result().orElseThrow(() -> new IllegalStateException("Unable to map GeneratorOptions")); + WorldSettings newWorldSettings = new WorldSettings("worldeditregentempworld", originalWorldData.b.getGameType(), originalWorldData.b.hardcore, originalWorldData.b.getDifficulty(), originalWorldData.b.e(), originalWorldData.b.getGameRules(), originalWorldData.b.g()); + WorldDataServer newWorldData = new WorldDataServer(newWorldSettings, newOpts, Lifecycle.stable()); + + //init world + freshNMSWorld = Fawe.get().getQueueHandler().sync((Supplier) () -> new WorldServer(server, server.executorService, session, newWorldData, originalNMSWorld.getDimensionKey(), originalNMSWorld.getTypeKey(), originalNMSWorld.getDimensionManager(), new RegenNoOpWorldLoadListener(), ((WorldDimension) newOpts.e().a(worldDimKey)).c(), originalNMSWorld.isDebugWorld(), seed, ImmutableList.of(), false, env, gen) { + @Override + public void doTick(BooleanSupplier booleansupplier) { //no ticking + } + }).get(); + freshNMSWorld.savingDisabled = true; + removeWorldFromWorldsMap(); + newWorldData.checkName(originalNMSWorld.worldDataServer.getName()); //rename to original world name + + freshChunkProvider = new ChunkProviderServer(freshNMSWorld, session, server.getDataFixer(), server.getDefinedStructureManager(), server.executorService, originalChunkProvider.chunkGenerator, freshNMSWorld.spigotConfig.viewDistance, server.isSyncChunkWrites(), new RegenNoOpWorldLoadListener(), () -> server.D().getWorldPersistentData()) { + // redirect to our protoChunks list + @Override + public IChunkAccess getChunkAt(int x, int z, ChunkStatus chunkstatus, boolean flag) { + return getProtoChunkAt(x, z); + } + }; + chunkProviderField.set(freshNMSWorld, freshChunkProvider); + + //generator + if (originalChunkProvider.getChunkGenerator() instanceof ChunkProviderFlat) { + GeneratorSettingsFlat generatorSettingFlat = (GeneratorSettingsFlat) generatorSettingFlatField.get(originalChunkProvider.getChunkGenerator()); + generator = new ChunkProviderFlat(generatorSettingFlat); + } else if (originalChunkProvider.getChunkGenerator() instanceof ChunkGeneratorAbstract) { + GeneratorSettingBase generatorSettingBase = (GeneratorSettingBase) generatorSettingBaseField.get(originalChunkProvider.getChunkGenerator()); + WorldChunkManager chunkManager = originalChunkProvider.getChunkGenerator().getWorldChunkManager(); + if (chunkManager instanceof WorldChunkManagerOverworld) { + chunkManager = fastOverWorldChunkManager(chunkManager); + } + generator = new ChunkGeneratorAbstract(chunkManager, seed, generatorSettingBase); + } else if (originalChunkProvider.getChunkGenerator() instanceof CustomChunkGenerator) { + ChunkGenerator delegate = (ChunkGenerator) delegateField.get(originalChunkProvider.getChunkGenerator()); + generator = delegate; + } else { + System.out.println("Unsupported generator type " + originalChunkProvider.getChunkGenerator().getClass().getName()); + return false; + } + if (originalNMSWorld.generator != null) { + // wrap custom world generator + generator = new CustomChunkGenerator(freshNMSWorld, generator, originalNMSWorld.generator); + generateConcurrent = originalNMSWorld.generator.isParallelCapable(); + } + + //lets start then + structureManager = server.getDefinedStructureManager(); + lightEngine = freshChunkProvider.getLightEngine(); + + return true; + } + + @Override + protected void cleanup() { + try { + session.close(); + } catch (Exception e) { + } + + //shutdown chunk provider + try { + Fawe.get().getQueueHandler().sync(() -> { + try { + freshChunkProvider.close(false); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } catch (Exception e) { + } + + //remove world from server + try { + removeWorldFromWorldsMap(); + } catch (Exception e) { + } + + //delete directory + try { + SafeFiles.tryHardToDeleteDir(tempDir); + } catch (Exception e) { + } + } + + @Override + protected ProtoChunk createProtoChunk(int x, int z) { + return new ProtoChunk(new ChunkCoordIntPair(x, z), ChunkConverter.a) { + public boolean generateFlatBedrock() { + return generateFlatBedrock; + } + }; + } + + @Override + protected Chunk createChunk(ProtoChunk protoChunk) { + return new Chunk(freshNMSWorld, protoChunk); + } + + @Override + protected ChunkStatusWrap getFullChunkStatus() { + return new ChunkStatusWrap(ChunkStatus.FULL); + } + + @Override + protected List getBlockPopulators() { + return originalNMSWorld.getWorld().getPopulators(); + } + + @Override + protected void populate(Chunk chunk, Random random, BlockPopulator pop) { + pop.populate(freshNMSWorld.getWorld(), random, chunk.bukkitChunk); + } + + @Override + protected IChunkCache initSourceQueueCache() { + return (chunkX, chunkZ) -> new BukkitGetBlocks_1_16_1(freshNMSWorld, chunkX, chunkZ) { + @Override + public Chunk ensureLoaded(World nmsWorld, int x, int z) { + return getChunkAt(x, z); + } + }; + } + + protected class ChunkStatusWrap extends Regenerator.ChunkStatusWrapper { + + private final ChunkStatus chunkStatus; + + public ChunkStatusWrap(ChunkStatus chunkStatus) { + this.chunkStatus = chunkStatus; + } + + @Override + public int requiredNeigborChunkRadius() { + return chunkStatus.f(); + } + + @Override + public String name() { + return chunkStatus.d(); + } + + @Override + public void processChunk(Long xz, List accessibleChunks) { + chunkStatus.a(freshNMSWorld, + generator, + structureManager, + lightEngine, + c -> CompletableFuture.completedFuture(Either.left(c)), + accessibleChunks); + } + } + + //util + private void removeWorldFromWorldsMap() { + Fawe.get().getQueueHandler().sync(() -> { + try { + Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + map.remove("worldeditregentempworld"); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + } + + private ResourceKey getWorldDimKey(org.bukkit.World.Environment env) { + switch (env) { + case NETHER: + return WorldDimension.THE_NETHER; + case THE_END: + return WorldDimension.THE_END; + case NORMAL: + default: + return WorldDimension.OVERWORLD; + } + } + + private Dynamic recursivelySetSeed(Dynamic dynamic, long seed, Set> seen) { + return !seen.add(dynamic) ? dynamic : dynamic.updateMapValues((pair) -> { + if (((Dynamic) pair.getFirst()).asString("").equals("seed")) { + return pair.mapSecond((v) -> { + return v.createLong(seed); + }); + } else { + return ((Dynamic) pair.getSecond()).getValue() instanceof NBTTagCompound ? pair.mapSecond((v) -> { + return this.recursivelySetSeed((Dynamic) v, seed, seen); + }) : pair; + } + }); + } + + private WorldChunkManager fastOverWorldChunkManager(WorldChunkManager chunkManager) throws Exception { + Field legacyBiomeInitLayerField = WorldChunkManagerOverworld.class.getDeclaredField("i"); + legacyBiomeInitLayerField.setAccessible(true); + Field largeBiomesField = WorldChunkManagerOverworld.class.getDeclaredField("j"); + largeBiomesField.setAccessible(true); + Field genLayerField = WorldChunkManagerOverworld.class.getDeclaredField("f"); + genLayerField.setAccessible(true); + Field areaLazyField = GenLayer.class.getDeclaredField("b"); + areaLazyField.setAccessible(true); + Method initAreaFactoryMethod = GenLayers.class.getDeclaredMethod("a", boolean.class, int.class, int.class, LongFunction.class); + initAreaFactoryMethod.setAccessible(true); + + //init new WorldChunkManagerOverworld + boolean legacyBiomeInitLayer = legacyBiomeInitLayerField.getBoolean(chunkManager); + boolean largebiomes = largeBiomesField.getBoolean(chunkManager); + chunkManager = new WorldChunkManagerOverworld(seed, legacyBiomeInitLayer, largebiomes); + + //replace genLayer + AreaFactory factory = (AreaFactory) initAreaFactoryMethod.invoke(null, legacyBiomeInitLayer, largebiomes ? 6 : 4, 4, (LongFunction) (l -> new FastWorldGenContextArea(seed, l))); + genLayerField.set(chunkManager, new FastGenLayer(factory)); + + return chunkManager; + } + + private static class FastWorldGenContextArea implements AreaContextTransformed { + + private final ConcurrentHashMap sharedAreaMap = new ConcurrentHashMap<>(); + private final NoiseGeneratorPerlin perlinNoise; + private final long magicrandom; + private final ConcurrentHashMap map = new ConcurrentHashMap<>(); //needed for multithreaded generation + + public FastWorldGenContextArea(long seed, long lconst) { + this.magicrandom = mix(seed, lconst); + this.perlinNoise = new NoiseGeneratorPerlin(new Random(seed)); + } + + @Override + public FastAreaLazy a(AreaTransformer8 var0) { + return new FastAreaLazy(sharedAreaMap, var0); + } + + @Override + public void a(long x, long z) { + long l = this.magicrandom; + l = LinearCongruentialGenerator.a(l, x); + l = LinearCongruentialGenerator.a(l, z); + l = LinearCongruentialGenerator.a(l, x); + l = LinearCongruentialGenerator.a(l, z); + this.map.put(Thread.currentThread().getId(), l); + } + + @Override + public int a(int y) { + long tid = Thread.currentThread().getId(); + long e = this.map.computeIfAbsent(tid, i -> 0L); + int mod = (int) Math.floorMod(e >> 24L, (long) y); + this.map.put(tid, LinearCongruentialGenerator.a(e, this.magicrandom)); + return mod; + } + + @Override + public NoiseGeneratorPerlin b() { + return this.perlinNoise; + } + + private static long mix(long seed, long lconst) { + long l1 = lconst; + l1 = LinearCongruentialGenerator.a(l1, lconst); + l1 = LinearCongruentialGenerator.a(l1, lconst); + l1 = LinearCongruentialGenerator.a(l1, lconst); + long l2 = seed; + l2 = LinearCongruentialGenerator.a(l2, l1); + l2 = LinearCongruentialGenerator.a(l2, l1); + l2 = LinearCongruentialGenerator.a(l2, l1); + return l2; + } + } + + private static class FastGenLayer extends GenLayer { + + private final FastAreaLazy areaLazy; + + public FastGenLayer(AreaFactory factory) throws Exception { + super(() -> null); + this.areaLazy = factory.make(); + } + + @Override + public BiomeBase a(int x, int z) { + BiomeBase biome = IRegistry.BIOME.fromId(this.areaLazy.a(x, z)); + if (biome == null) + return Biomes.b; + return biome; + } + } + + private static class FastAreaLazy implements Area { + + private final AreaTransformer8 transformer; + //ConcurrentHashMap is 50% faster that Long2IntLinkedOpenHashMap in a syncronized context + //using a map for each thread worsens the performance significantly due to cache misses (factor 5) + private final ConcurrentHashMap sharedMap; + + public FastAreaLazy(ConcurrentHashMap sharedMap, AreaTransformer8 transformer) { + this.sharedMap = sharedMap; + this.transformer = transformer; + } + + @Override + public int a(int x, int z) { + long zx = ChunkCoordIntPair.pair(x, z); + return this.sharedMap.computeIfAbsent(zx, i -> this.transformer.apply(x, z)); + } + } + + private static class RegenNoOpWorldLoadListener implements WorldLoadListener { + + private RegenNoOpWorldLoadListener() { + } + + @Override + public void a(ChunkCoordIntPair chunkCoordIntPair) { + } + + @Override + public void a(ChunkCoordIntPair chunkCoordIntPair, @Nullable ChunkStatus chunkStatus) { + } + + @Override + public void b() { + } + } +} diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_16_R2.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_16_R2.java new file mode 100644 index 000000000..af58d56cf --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_16_R2.java @@ -0,0 +1,529 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.regen; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.beta.IChunkCache; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.bukkit.adapter.mc1_16_2.BukkitGetBlocks_1_16_2; +import com.google.common.collect.ImmutableList; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Dynamic; +import com.mojang.serialization.Lifecycle; +import com.sk89q.worldedit.bukkit.adapter.Regenerator; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.io.file.SafeFiles; +import com.sk89q.worldedit.world.RegenOptions; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BooleanSupplier; +import java.util.function.LongFunction; +import java.util.function.Supplier; +import javax.annotation.Nullable; +import net.minecraft.server.v1_16_R2.Area; +import net.minecraft.server.v1_16_R2.AreaContextTransformed; +import net.minecraft.server.v1_16_R2.AreaFactory; +import net.minecraft.server.v1_16_R2.AreaTransformer8; +import net.minecraft.server.v1_16_R2.BiomeBase; +import net.minecraft.server.v1_16_R2.BiomeRegistry; +import net.minecraft.server.v1_16_R2.Chunk; +import net.minecraft.server.v1_16_R2.ChunkConverter; +import net.minecraft.server.v1_16_R2.ChunkCoordIntPair; +import net.minecraft.server.v1_16_R2.ChunkGenerator; +import net.minecraft.server.v1_16_R2.ChunkGeneratorAbstract; +import net.minecraft.server.v1_16_R2.ChunkProviderFlat; +import net.minecraft.server.v1_16_R2.ChunkProviderServer; +import net.minecraft.server.v1_16_R2.ChunkStatus; +import net.minecraft.server.v1_16_R2.Convertable; +import net.minecraft.server.v1_16_R2.DefinedStructureManager; +import net.minecraft.server.v1_16_R2.DynamicOpsNBT; +import net.minecraft.server.v1_16_R2.GenLayer; +import net.minecraft.server.v1_16_R2.GenLayers; +import net.minecraft.server.v1_16_R2.GeneratorSettingBase; +import net.minecraft.server.v1_16_R2.GeneratorSettings; +import net.minecraft.server.v1_16_R2.GeneratorSettingsFlat; +import net.minecraft.server.v1_16_R2.IChunkAccess; +import net.minecraft.server.v1_16_R2.IRegistry; +import net.minecraft.server.v1_16_R2.IRegistryCustom; +import net.minecraft.server.v1_16_R2.LightEngineThreaded; +import net.minecraft.server.v1_16_R2.LinearCongruentialGenerator; +import net.minecraft.server.v1_16_R2.MinecraftServer; +import net.minecraft.server.v1_16_R2.NBTBase; +import net.minecraft.server.v1_16_R2.NBTTagCompound; +import net.minecraft.server.v1_16_R2.NoiseGeneratorPerlin; +import net.minecraft.server.v1_16_R2.ProtoChunk; +import net.minecraft.server.v1_16_R2.RegistryReadOps; +import net.minecraft.server.v1_16_R2.ResourceKey; +import net.minecraft.server.v1_16_R2.World; +import net.minecraft.server.v1_16_R2.WorldChunkManager; +import net.minecraft.server.v1_16_R2.WorldChunkManagerOverworld; +import net.minecraft.server.v1_16_R2.WorldDataServer; +import net.minecraft.server.v1_16_R2.WorldDimension; +import net.minecraft.server.v1_16_R2.WorldLoadListener; +import net.minecraft.server.v1_16_R2.WorldServer; +import net.minecraft.server.v1_16_R2.WorldSettings; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_16_R2.CraftServer; +import org.bukkit.craftbukkit.v1_16_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R2.generator.CustomChunkGenerator; +import org.bukkit.generator.BlockPopulator; + +public class Regen_v1_16_R2 extends Regenerator { + + private static final Field serverWorldsField; + private static final Field worldPaperConfigField; + private static final Field flatBedrockField; + private static final Field generatorSettingBaseSupplierField; + private static final Field generatorSettingFlatField; + private static final Field delegateField; + private static final Field chunkProviderField; + + //list of chunk stati in correct order without FULL + private static final Map chunkStati = new LinkedHashMap<>(); + + static { + chunkStati.put(ChunkStatus.EMPTY, Regenerator.Concurrency.FULL); // radius -1, does nothing + chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Regenerator.Concurrency.NONE); // uses unsynchronized maps + chunkStati.put(ChunkStatus.STRUCTURE_REFERENCES, Regenerator.Concurrency.FULL); // radius 8, but no writes to other chunks, only current chunk + chunkStati.put(ChunkStatus.BIOMES, Regenerator.Concurrency.FULL); // radius 0 + chunkStati.put(ChunkStatus.NOISE, Regenerator.Concurrency.RADIUS); // radius 8 + chunkStati.put(ChunkStatus.SURFACE, Regenerator.Concurrency.FULL); // radius 0 + chunkStati.put(ChunkStatus.CARVERS, Regenerator.Concurrency.NONE); // radius 0, but RADIUS and FULL change results + chunkStati.put(ChunkStatus.LIQUID_CARVERS, Regenerator.Concurrency.NONE); // radius 0, but RADIUS and FULL change results + chunkStati.put(ChunkStatus.FEATURES, Regenerator.Concurrency.NONE); // uses unsynchronized maps + chunkStati.put(ChunkStatus.LIGHT, Regenerator.Concurrency.FULL); // radius 1, but no writes to other chunks, only current chunk + chunkStati.put(ChunkStatus.SPAWN, Regenerator.Concurrency.FULL); // radius 0 + chunkStati.put(ChunkStatus.HEIGHTMAPS, Regenerator.Concurrency.FULL); // radius 0 + + try { + serverWorldsField = CraftServer.class.getDeclaredField("worlds"); + serverWorldsField.setAccessible(true); + + Field tmpPaperConfigField = null; + Field tmpFlatBedrockField = null; + try { //only present on paper + tmpPaperConfigField = World.class.getDeclaredField("paperConfig"); + tmpPaperConfigField.setAccessible(true); + + tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock"); + tmpFlatBedrockField.setAccessible(true); + } catch (Exception e) { + tmpPaperConfigField = null; + tmpFlatBedrockField = null; + } + worldPaperConfigField = tmpPaperConfigField; + flatBedrockField = tmpFlatBedrockField; + + generatorSettingBaseSupplierField = ChunkGeneratorAbstract.class.getDeclaredField("h"); + generatorSettingBaseSupplierField.setAccessible(true); + + generatorSettingFlatField = ChunkProviderFlat.class.getDeclaredField("e"); + generatorSettingFlatField.setAccessible(true); + + delegateField = CustomChunkGenerator.class.getDeclaredField("delegate"); + delegateField.setAccessible(true); + + chunkProviderField = WorldServer.class.getDeclaredField("chunkProvider"); + chunkProviderField.setAccessible(true); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + //runtime + private WorldServer originalNMSWorld; + private ChunkProviderServer originalChunkProvider; + private WorldServer freshNMSWorld; + private ChunkProviderServer freshChunkProvider; + private Convertable.ConversionSession session; + private DefinedStructureManager structureManager; + private LightEngineThreaded lightEngine; + private ChunkGenerator generator; + + private Path tempDir; + + private boolean generateFlatBedrock = false; + + public Regen_v1_16_R2(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) { + super(originalBukkitWorld, region, target, options); + } + + @Override + protected boolean prepare() { + this.originalNMSWorld = ((CraftWorld) originalBukkitWorld).getHandle(); + originalChunkProvider = originalNMSWorld.getChunkProvider(); + if (!(originalChunkProvider instanceof ChunkProviderServer)) { + return false; + } + + //flat bedrock? (only on paper) + try { + generateFlatBedrock = flatBedrockField.getBoolean(worldPaperConfigField.get(originalNMSWorld)); + } catch (Exception ignored) { + } + + seed = options.getSeed().orElse(originalNMSWorld.getSeed()); + chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c)); + + return true; + } + + @Override + protected boolean initNewWorld() throws Exception { + //world folder + tempDir = java.nio.file.Files.createTempDirectory("WorldEditWorldGen"); + + //prepare for world init (see upstream implementation for reference) + org.bukkit.World.Environment env = originalBukkitWorld.getEnvironment(); + org.bukkit.generator.ChunkGenerator gen = originalBukkitWorld.getGenerator(); + Convertable convertable = Convertable.a(tempDir); + ResourceKey worldDimKey = getWorldDimKey(env); + session = convertable.c("worldeditregentempworld", worldDimKey); + WorldDataServer originalWorldData = originalNMSWorld.worldDataServer; + + MinecraftServer server = originalNMSWorld.getServer().getServer(); + WorldDataServer levelProperties = (WorldDataServer) server.getSaveData(); + RegistryReadOps nbtRegOps = RegistryReadOps.a(DynamicOpsNBT.a, server.dataPackResources.h(), IRegistryCustom.b()); + GeneratorSettings newOpts = GeneratorSettings.a.encodeStart(nbtRegOps, levelProperties.getGeneratorSettings()).flatMap(tag -> GeneratorSettings.a.parse(this.recursivelySetSeed(new Dynamic<>(nbtRegOps, tag), seed, new HashSet<>()))).result().orElseThrow(() -> new IllegalStateException("Unable to map GeneratorOptions")); + WorldSettings newWorldSettings = new WorldSettings("worldeditregentempworld", originalWorldData.b.getGameType(), originalWorldData.b.hardcore, originalWorldData.b.getDifficulty(), originalWorldData.b.e(), originalWorldData.b.getGameRules(), originalWorldData.b.g()); + WorldDataServer newWorldData = new WorldDataServer(newWorldSettings, newOpts, Lifecycle.stable()); + + //init world + freshNMSWorld = Fawe.get().getQueueHandler().sync((Supplier) () -> new WorldServer(server, server.executorService, session, newWorldData, originalNMSWorld.getDimensionKey(), originalNMSWorld.getDimensionManager(), new RegenNoOpWorldLoadListener(), ((WorldDimension) newOpts.d().a(worldDimKey)).c(), originalNMSWorld.isDebugWorld(), seed, ImmutableList.of(), false, env, gen) { + @Override + public void doTick(BooleanSupplier booleansupplier) { //no ticking + } + }).get(); + freshNMSWorld.savingDisabled = true; + removeWorldFromWorldsMap(); + newWorldData.checkName(originalNMSWorld.worldDataServer.getName()); //rename to original world name + + freshChunkProvider = new ChunkProviderServer(freshNMSWorld, session, server.getDataFixer(), server.getDefinedStructureManager(), server.executorService, originalChunkProvider.chunkGenerator, freshNMSWorld.spigotConfig.viewDistance, server.isSyncChunkWrites(), new RegenNoOpWorldLoadListener(), () -> server.E().getWorldPersistentData()) { + // redirect to our protoChunks list + @Override + public IChunkAccess getChunkAt(int x, int z, ChunkStatus chunkstatus, boolean flag) { + return getProtoChunkAt(x, z); + } + }; + chunkProviderField.set(freshNMSWorld, freshChunkProvider); + + //generator + if (originalChunkProvider.getChunkGenerator() instanceof ChunkProviderFlat) { + GeneratorSettingsFlat generatorSettingFlat = (GeneratorSettingsFlat) generatorSettingFlatField.get(originalChunkProvider.getChunkGenerator()); + generator = new ChunkProviderFlat(generatorSettingFlat); + } else if (originalChunkProvider.getChunkGenerator() instanceof ChunkGeneratorAbstract) { + Supplier generatorSettingBaseSupplier = (Supplier) generatorSettingBaseSupplierField.get(originalChunkProvider.getChunkGenerator()); + WorldChunkManager chunkManager = originalChunkProvider.getChunkGenerator().getWorldChunkManager(); + if (chunkManager instanceof WorldChunkManagerOverworld) { + chunkManager = fastOverWorldChunkManager(chunkManager); + } + generator = new ChunkGeneratorAbstract(chunkManager, seed, generatorSettingBaseSupplier); + } else if (originalChunkProvider.getChunkGenerator() instanceof CustomChunkGenerator) { + ChunkGenerator delegate = (ChunkGenerator) delegateField.get(originalChunkProvider.getChunkGenerator()); + generator = delegate; + } else { + System.out.println("Unsupported generator type " + originalChunkProvider.getChunkGenerator().getClass().getName()); + return false; + } + if (originalNMSWorld.generator != null) { + // wrap custom world generator + generator = new CustomChunkGenerator(freshNMSWorld, generator, originalNMSWorld.generator); + generateConcurrent = originalNMSWorld.generator.isParallelCapable(); + } + + //lets start then + structureManager = server.getDefinedStructureManager(); + lightEngine = freshChunkProvider.getLightEngine(); + + return true; + } + + @Override + protected void cleanup() { + try { + session.close(); + } catch (Exception e) { + } + + //shutdown chunk provider + try { + Fawe.get().getQueueHandler().sync(() -> { + try { + freshChunkProvider.close(false); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } catch (Exception e) { + } + + //remove world from server + try { + Fawe.get().getQueueHandler().sync(() -> { + removeWorldFromWorldsMap(); + }); + } catch (Exception e) { + } + + //delete directory + try { + SafeFiles.tryHardToDeleteDir(tempDir); + } catch (Exception e) { + } + } + + @Override + protected ProtoChunk createProtoChunk(int x, int z) { + return new ProtoChunk(new ChunkCoordIntPair(x, z), ChunkConverter.a) { + public boolean generateFlatBedrock() { + return generateFlatBedrock; + } + }; + } + + @Override + protected Chunk createChunk(ProtoChunk protoChunk) { + return new Chunk(freshNMSWorld, protoChunk); + } + + @Override + protected ChunkStatusWrap getFullChunkStatus() { + return new ChunkStatusWrap(ChunkStatus.FULL); + } + + @Override + protected List getBlockPopulators() { + return originalNMSWorld.getWorld().getPopulators(); + } + + @Override + protected void populate(Chunk chunk, Random random, BlockPopulator pop) { + pop.populate(freshNMSWorld.getWorld(), random, chunk.bukkitChunk); + } + + @Override + protected IChunkCache initSourceQueueCache() { + return (chunkX, chunkZ) -> new BukkitGetBlocks_1_16_2(freshNMSWorld, chunkX, chunkZ) { + @Override + public Chunk ensureLoaded(World nmsWorld, int x, int z) { + return getChunkAt(x, z); + } + }; + } + + protected class ChunkStatusWrap extends Regenerator.ChunkStatusWrapper { + + private final ChunkStatus chunkStatus; + + public ChunkStatusWrap(ChunkStatus chunkStatus) { + this.chunkStatus = chunkStatus; + } + + @Override + public int requiredNeigborChunkRadius() { + return chunkStatus.f(); + } + + @Override + public String name() { + return chunkStatus.d(); + } + + @Override + public void processChunk(Long xz, List accessibleChunks) { + chunkStatus.a(freshNMSWorld, + generator, + structureManager, + lightEngine, + c -> CompletableFuture.completedFuture(Either.left(c)), + accessibleChunks); + } + } + + //util + private void removeWorldFromWorldsMap() { + Fawe.get().getQueueHandler().sync(() -> { + try { + Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + map.remove("worldeditregentempworld"); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + } + + private ResourceKey getWorldDimKey(org.bukkit.World.Environment env) { + switch (env) { + case NETHER: + return WorldDimension.THE_NETHER; + case THE_END: + return WorldDimension.THE_END; + case NORMAL: + default: + return WorldDimension.OVERWORLD; + } + } + + private Dynamic recursivelySetSeed(Dynamic dynamic, long seed, Set> seen) { + return !seen.add(dynamic) ? dynamic : dynamic.updateMapValues((pair) -> { + if (((Dynamic) pair.getFirst()).asString("").equals("seed")) { + return pair.mapSecond((v) -> { + return v.createLong(seed); + }); + } else { + return ((Dynamic) pair.getSecond()).getValue() instanceof NBTTagCompound ? pair.mapSecond((v) -> { + return this.recursivelySetSeed((Dynamic) v, seed, seen); + }) : pair; + + } + }); + } + + private WorldChunkManager fastOverWorldChunkManager(WorldChunkManager chunkManager) throws Exception { + Field legacyBiomeInitLayerField = WorldChunkManagerOverworld.class.getDeclaredField("i"); + legacyBiomeInitLayerField.setAccessible(true); + Field largeBiomesField = WorldChunkManagerOverworld.class.getDeclaredField("j"); + largeBiomesField.setAccessible(true); + Field biomeRegistryField = WorldChunkManagerOverworld.class.getDeclaredField("k"); + biomeRegistryField.setAccessible(true); + Field genLayerField = WorldChunkManagerOverworld.class.getDeclaredField("f"); + genLayerField.setAccessible(true); + Field areaLazyField = GenLayer.class.getDeclaredField("b"); + areaLazyField.setAccessible(true); + Method initAreaFactoryMethod = GenLayers.class.getDeclaredMethod("a", boolean.class, int.class, int.class, LongFunction.class); + initAreaFactoryMethod.setAccessible(true); + + //init new WorldChunkManagerOverworld + boolean legacyBiomeInitLayer = legacyBiomeInitLayerField.getBoolean(chunkManager); + boolean largebiomes = largeBiomesField.getBoolean(chunkManager); + IRegistry biomeRegistry = (IRegistry) biomeRegistryField.get(chunkManager); + chunkManager = new WorldChunkManagerOverworld(seed, legacyBiomeInitLayer, largebiomes, biomeRegistry); + + //replace genLayer + AreaFactory factory = (AreaFactory) initAreaFactoryMethod.invoke(null, legacyBiomeInitLayer, largebiomes ? 6 : 4, 4, (LongFunction) (l -> new FastWorldGenContextArea(seed, l))); + genLayerField.set(chunkManager, new FastGenLayer(factory)); + + return chunkManager; + } + + private static class FastWorldGenContextArea implements AreaContextTransformed { + + private final ConcurrentHashMap sharedAreaMap = new ConcurrentHashMap<>(); + private final NoiseGeneratorPerlin perlinNoise; + private final long magicrandom; + private final ConcurrentHashMap map = new ConcurrentHashMap<>(); //needed for multithreaded generation + + public FastWorldGenContextArea(long seed, long lconst) { + this.magicrandom = mix(seed, lconst); + this.perlinNoise = new NoiseGeneratorPerlin(new Random(seed)); + } + + @Override + public FastAreaLazy a(AreaTransformer8 var0) { + return new FastAreaLazy(sharedAreaMap, var0); + } + + @Override + public void a(long x, long z) { + long l = this.magicrandom; + l = LinearCongruentialGenerator.a(l, x); + l = LinearCongruentialGenerator.a(l, z); + l = LinearCongruentialGenerator.a(l, x); + l = LinearCongruentialGenerator.a(l, z); + this.map.put(Thread.currentThread().getId(), l); + } + + @Override + public int a(int y) { + long tid = Thread.currentThread().getId(); + long e = this.map.computeIfAbsent(tid, i -> 0L); + int mod = (int) Math.floorMod(e >> 24L, (long) y); + this.map.put(tid, LinearCongruentialGenerator.a(e, this.magicrandom)); + return mod; + } + + @Override + public NoiseGeneratorPerlin b() { + return this.perlinNoise; + } + + private static long mix(long seed, long lconst) { + long l1 = lconst; + l1 = LinearCongruentialGenerator.a(l1, lconst); + l1 = LinearCongruentialGenerator.a(l1, lconst); + l1 = LinearCongruentialGenerator.a(l1, lconst); + long l2 = seed; + l2 = LinearCongruentialGenerator.a(l2, l1); + l2 = LinearCongruentialGenerator.a(l2, l1); + l2 = LinearCongruentialGenerator.a(l2, l1); + return l2; + } + } + + private static class FastGenLayer extends GenLayer { + + private final FastAreaLazy areaLazy; + + public FastGenLayer(AreaFactory factory) throws Exception { + super(() -> null); + this.areaLazy = factory.make(); + } + + @Override + public BiomeBase a(IRegistry registry, int x, int z) { + ResourceKey key = BiomeRegistry.a(this.areaLazy.a(x, z)); + if (key == null) + return registry.a(BiomeRegistry.a(0)); + BiomeBase biome = registry.a(key); + if (biome == null) + return registry.a(BiomeRegistry.a(0)); + return biome; + } + } + + private static class FastAreaLazy implements Area { + + private final AreaTransformer8 transformer; + //ConcurrentHashMap is 50% faster that Long2IntLinkedOpenHashMap in a syncronized context + //using a map for each thread worsens the performance significantly due to cache misses (factor 5) + private final ConcurrentHashMap sharedMap; + + public FastAreaLazy(ConcurrentHashMap sharedMap, AreaTransformer8 transformer) { + this.sharedMap = sharedMap; + this.transformer = transformer; + } + + @Override + public int a(int x, int z) { + long zx = ChunkCoordIntPair.pair(x, z); + return this.sharedMap.computeIfAbsent(zx, i -> this.transformer.apply(x, z)); + } + } + + private static class RegenNoOpWorldLoadListener implements WorldLoadListener { + + private RegenNoOpWorldLoadListener() { + } + + @Override + public void a(ChunkCoordIntPair chunkCoordIntPair) { + } + + @Override + public void a(ChunkCoordIntPair chunkCoordIntPair, @Nullable ChunkStatus chunkStatus) { + } + + @Override + public void b() { + } + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index a5c184fb1..d357d111d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -616,6 +616,7 @@ public class RegionCommands { try { session.setMask((Mask) null); session.setSourceMask((Mask) null); + actor.printInfo(TranslatableComponent.of("fawe.regen.time")); success = world.regenerate(region, editSession); } finally { session.setMask(mask); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java index 21b2cd39f..1c9348fa9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java @@ -186,7 +186,7 @@ public interface Extent extends InputExtent, OutputExtent { } default boolean regenerateChunk(int x, int z, @Nullable BiomeType type, @Nullable Long seed) { - throw new UnsupportedOperationException("TODO NOT IMPLEMENTED: " + isWorld()); + return false; } /* diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java index 1d2ff9e97..747675232 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java @@ -31,6 +31,7 @@ import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; @@ -226,6 +227,10 @@ public class NullWorld extends AbstractWorld { @Override public void sendFakeChunk(@Nullable Player player, ChunkPacket packet) { + } + @Override + public boolean regenerate(Region region, Extent extent, RegenOptions options) { + return false; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java index f0771063a..05ef1e5c7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java @@ -31,6 +31,8 @@ import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.internal.util.DeprecationUtil; +import com.sk89q.worldedit.internal.util.NonAbstractForCompatibility; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; @@ -218,7 +220,44 @@ public interface World extends Extent, Keyed, IChunkCache { * @param editSession the {@link EditSession} * @return true if re-generation was successful */ - boolean regenerate(Region region, EditSession editSession); + default boolean regenerate(Region region, EditSession editSession) { + return regenerate(region, editSession, RegenOptions.builder().build()); + } + + /** + * Regenerate an area. + * + * @param region the region + * @param extent the {@link Extent} + * @return true if re-generation was successful + */ + default boolean regenerate(Region region, Extent extent) { + return regenerate(region, extent, RegenOptions.builder().build()); + } + + /** + * Regenerate an area. + * + * @param region the region + * @param extent the {@link Extent} + * @param options the regeneration options + * @return true if regeneration was successful + * @apiNote This must be overridden by new subclasses. See {@link NonAbstractForCompatibility} + * for details + */ + @NonAbstractForCompatibility( + delegateName = "regenerate", + delegateParams = { Region.class, EditSession.class } + ) + default boolean regenerate(Region region, Extent extent, RegenOptions options) { + DeprecationUtil.checkDelegatingOverride(getClass()); + if (extent instanceof EditSession) { + return regenerate(region, (EditSession) extent); + } + throw new UnsupportedOperationException("This World class (" + + getClass().getName() + + ") does not implement the general Extent variant of this method"); + } /** * Generate a tree at the given position. diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index de060a1ed..a074f2ed0 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -165,6 +165,8 @@ "fawe.tips.tip.biome.pattern": "Tip: The #biome[forest] pattern can be used in any command", "fawe.tips.tip.biome.mask": "Tip: Restrict to a biome with the `$jungle` mask", + "fawe.regen.time": "Regenerating region, this might take a while!", + "worldedit.expand.description.vert": "Vertically expand the selection to world limits.", "worldedit.expand.expanded": "Region expanded {0} blocks", "worldedit.expand.expanded.vert": "Region expanded {0} blocks (top-to-bottom).", From 4cea303eb79aff65fdfdeb1308b95950acc5cf1e Mon Sep 17 00:00:00 2001 From: N0tMyFaultOG Date: Sun, 11 Oct 2020 13:37:11 +0200 Subject: [PATCH 4/8] Update logo --- .github/ISSUE_TEMPLATE/bug-report.md | 8 +++++--- README.md | 2 +- fawe-logo.png | Bin 0 -> 17596 bytes 3 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 fawe-logo.png diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 5b69cda82..87c970593 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -19,10 +19,12 @@ assignees: '' +**/fawe debugpaste**: + **Required Information** -- FAWE Version Number: -- Spigot/Paper Version Number: -- Minecraft Version: [e.g. 1.16.3] +- FAWE Version Number (`/version FastAsyncWorldEdit`): +- Spigot/Paper Version Number (`/version`): +- Minecraft Version: [e.g. 1.16.3] **Describe the bug** A clear and concise description of what the bug is. diff --git a/README.md b/README.md index 2ef4aa97f..703bb9a34 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

--- diff --git a/fawe-logo.png b/fawe-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ddf2f7021e254a1b7173fca92ebf13530d666fd7 GIT binary patch literal 17596 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 GQ~w7SdZx|* literal 0 HcmV?d00001 From 8c26c555017636449a41e4cb0396c2c4e6fa9772 Mon Sep 17 00:00:00 2001 From: N0tMyFaultOG Date: Sun, 11 Oct 2020 13:39:59 +0200 Subject: [PATCH 5/8] Fix scaling --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 703bb9a34..8dcc311d0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

--- From 6d45169d9d968a1be1abd72946df9769642e59ca Mon Sep 17 00:00:00 2001 From: wea_ondara Date: Sat, 10 Oct 2020 15:41:34 +0200 Subject: [PATCH 6/8] fix tabcomplete for biomes and tree types, fixes #457 --- .../platform/binding/ConsumeBindings.java | 64 ------------------- 1 file changed, 64 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/ConsumeBindings.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/ConsumeBindings.java index 42149269d..61e855538 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/ConsumeBindings.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/ConsumeBindings.java @@ -3,17 +3,14 @@ package com.sk89q.worldedit.extension.platform.binding; import com.boydti.fawe.Fawe; import com.boydti.fawe.config.Caption; import com.boydti.fawe.util.MainUtil; -import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.image.ImageUtil; import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.command.util.annotation.Confirm; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.NoMatchException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.annotation.Selection; @@ -23,20 +20,13 @@ import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Identifiable; -import com.sk89q.worldedit.util.TreeGenerator; -import com.sk89q.worldedit.util.TreeGenerator.TreeType; import com.sk89q.worldedit.world.World; -import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.biome.BiomeTypes; -import com.sk89q.worldedit.world.biome.Biomes; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; -import com.sk89q.worldedit.world.registry.BiomeRegistry; import org.enginehub.piston.inject.InjectedValueAccess; -import java.util.Collection; import java.util.UUID; public class ConsumeBindings extends Bindings { @@ -167,58 +157,4 @@ public class ConsumeBindings extends Bindings { throw new InputParseException(e.getMessage()); } } - - /** - * Gets an {@link TreeType} from a {@link Binding}. - * - * @param argument the context - * @return a TreeType - * @throws WorldEditException on error - */ - @Binding - public TreeGenerator.TreeType getTreeType(String argument) throws WorldEditException { - if (argument != null) { - TreeGenerator.TreeType type = TreeGenerator.lookup(argument); - if (type != null) { - return type; - } else { - throw new InputParseException( - String.format("Can't recognize tree type '%s' -- choose from: %s", argument, - TreeGenerator.TreeType.getPrimaryAliases())); - } - } else { - return TreeGenerator.TreeType.TREE; - } - } - - /** - * Gets an {@link BiomeType} from a {@link Binding}. - * - * @param argument the context - * @return a BiomeType - * @throws WorldEditException on error - */ - @Binding - public BiomeType getBiomeType(String argument) throws WorldEditException { - if (argument != null) { - - if (MathMan.isInteger(argument)) { - return BiomeTypes.getLegacy(Integer.parseInt(argument)); - } - BiomeRegistry biomeRegistry = WorldEdit.getInstance().getPlatformManager() - .queryCapability(Capability.GAME_HOOKS).getRegistries().getBiomeRegistry(); - Collection knownBiomes = BiomeType.REGISTRY.values(); - BiomeType biome = Biomes.findBiomeByName(knownBiomes, argument, biomeRegistry); - if (biome != null) { - return biome; - } else { - throw new InputParseException( - String.format("Can't recognize biome type '%s' -- use /biomelist to list available types", argument)); - } - } else { - throw new InputParseException( - "This command takes a 'default' biome if one is not set, except there is no particular " + - "biome that should be 'default', so the command should not be taking a default biome"); - } - } } From b5f41501e46d31c40223e05baad4f53751e1efb5 Mon Sep 17 00:00:00 2001 From: wea_ondara Date: Sun, 11 Oct 2020 13:28:47 +0200 Subject: [PATCH 7/8] fix tabcomplete for brushes and possible many other commands --- .../worldedit/extension/platform/PlatformCommandManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java index 2bb552c74..c221acb6b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java @@ -850,7 +850,7 @@ public final class PlatformCommandManager { event.setSuggestions(suggestions.stream() .map(suggestion -> { - int noSlashLength = arguments.length() - 1; + int noSlashLength = arguments.length(); Substring original = suggestion.getReplacedArgument() == split.size() ? Substring.from(arguments, noSlashLength, noSlashLength) : split.get(suggestion.getReplacedArgument()); From 4127e83749426068abebab2112607b3d9ecca7bc Mon Sep 17 00:00:00 2001 From: Aurora Date: Mon, 12 Oct 2020 11:57:39 +0200 Subject: [PATCH 8/8] Don't paste ComplexEntityParts --- .../worldedit/function/operation/ForwardExtentCopy.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java index 0ac331042..ae7fd5dcc 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java @@ -382,10 +382,11 @@ public class ForwardExtentCopy implements Operation { List entities; if (copyingEntities) { // filter players since they can't be copied - entities = source.getEntities(region) - .stream() - .filter(e -> e.getType() != EntityTypes.PLAYER) - .collect(Collectors.toList()); + entities = source.getEntities(region); + entities.removeIf(entity -> { + EntityProperties properties = entity.getFacet(EntityProperties.class); + return properties != null && !properties.isPasteable(); + }); } else { entities = Collections.emptyList(); }