From e67da40c98d8a262182c7e28ed2eead9fa7ec42e Mon Sep 17 00:00:00 2001 From: KidsDontPlay Date: Wed, 25 Sep 2019 22:30:41 +0200 Subject: [PATCH 1/9] finit --- .gitignore | 74 +--- build.gradle | 97 +++-- gradle.properties | 1 + gradle/wrapper/gradle-wrapper.jar | Bin 52271 -> 54708 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 78 ++-- gradlew.bat | 14 +- src/main/java/kdp/blockdrops/BlockDrops.java | 223 +++++++++++ src/main/java/kdp/blockdrops/Category.java | 76 ++++ src/main/java/kdp/blockdrops/Drop.java | 82 ++++ src/main/java/kdp/blockdrops/DropRecipe.java | 62 +++ src/main/java/kdp/blockdrops/Plugin.java | 41 ++ .../java/mrriegel/blockdrops/BlockDrops.java | 369 ------------------ .../java/mrriegel/blockdrops/Category.java | 60 --- src/main/java/mrriegel/blockdrops/Plugin.java | 29 -- .../java/mrriegel/blockdrops/Wrapper.java | 137 ------- .../java/mrriegel/blockdrops/util/Drop.java | 69 ---- .../blockdrops/util/FakeClientPlayer.java | 40 -- .../blockdrops/util/FakeClientWorld.java | 98 ----- .../blockdrops/util/StackWrapper.java | 33 -- .../mrriegel/blockdrops/util/WrapperJson.java | 86 ---- src/main/resources/META-INF/mods.toml | 27 ++ src/main/resources/mcmod.info | 16 - src/main/resources/pack.mcmeta | 7 + 24 files changed, 653 insertions(+), 1069 deletions(-) mode change 100755 => 100644 gradlew.bat create mode 100644 src/main/java/kdp/blockdrops/BlockDrops.java create mode 100644 src/main/java/kdp/blockdrops/Category.java create mode 100644 src/main/java/kdp/blockdrops/Drop.java create mode 100644 src/main/java/kdp/blockdrops/DropRecipe.java create mode 100644 src/main/java/kdp/blockdrops/Plugin.java delete mode 100644 src/main/java/mrriegel/blockdrops/BlockDrops.java delete mode 100644 src/main/java/mrriegel/blockdrops/Category.java delete mode 100644 src/main/java/mrriegel/blockdrops/Plugin.java delete mode 100644 src/main/java/mrriegel/blockdrops/Wrapper.java delete mode 100644 src/main/java/mrriegel/blockdrops/util/Drop.java delete mode 100644 src/main/java/mrriegel/blockdrops/util/FakeClientPlayer.java delete mode 100644 src/main/java/mrriegel/blockdrops/util/FakeClientWorld.java delete mode 100644 src/main/java/mrriegel/blockdrops/util/StackWrapper.java delete mode 100644 src/main/java/mrriegel/blockdrops/util/WrapperJson.java create mode 100644 src/main/resources/META-INF/mods.toml delete mode 100644 src/main/resources/mcmod.info create mode 100644 src/main/resources/pack.mcmeta diff --git a/.gitignore b/.gitignore index e844c7f..12f8644 100644 --- a/.gitignore +++ b/.gitignore @@ -1,63 +1,25 @@ -## gradle -/.gradle -/build - -## eclipse -/.settings -/.metadata -/.classpath -/.project -/bin -/run -/eclipse - +# eclipse +bin *.launch +.settings +.metadata +.classpath +.project -#####=== JetBrains ===##### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio - -*.iml - -## Directory-based project format: -.idea/ -# if you remove the above rule, at least ignore the following: - -# User-specific stuff: -# .idea/workspace.xml -# .idea/tasks.xml -# .idea/dictionaries - -# Sensitive or high-churn files: -# .idea/dataSources.ids -# .idea/dataSources.xml -# .idea/sqlDataSources.xml -# .idea/dynamic.xml -# .idea/uiDesigner.xml - -# Gradle: -# .idea/gradle.xml -# .idea/libraries - -# Mongo Explorer plugin: -# .idea/mongoSettings.xml - -## File-based project format: +# idea +out *.ipr *.iws +*.iml +.idea -## Plugin-specific files: - -# IntelliJ -/out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml +# gradle +build +.gradle -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties +# other +eclipse +run +# Files from Forge MDK +forge*changelog.txt diff --git a/build.gradle b/build.gradle index 5fb7d29..14b14f1 100644 --- a/build.gradle +++ b/build.gradle @@ -1,54 +1,93 @@ buildscript { repositories { + maven { url = 'https://files.minecraftforge.net/maven' } jcenter() - maven { url = "http://files.minecraftforge.net/maven" } + mavenCentral() } dependencies { - classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' + classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true } } -apply plugin: 'net.minecraftforge.gradle.forge' +apply plugin: 'net.minecraftforge.gradle' +apply plugin: 'eclipse' +apply plugin: 'maven-publish' +apply plugin: 'idea' + Properties props = new Properties() props.load(new BufferedReader(new FileReader(file('../gradle.properties')))) -version = "${props.get('mc_version')}-1.4.0" -group = "mrriegel.blockdrops" -archivesBaseName = "blockdrops" +version = "${props.get('mc_version')}-1.0.0" +group = 'kdp.blockdrops' +archivesBaseName = 'blockdrops' -sourceCompatibility = targetCompatibility = '1.8' -compileJava { - sourceCompatibility = targetCompatibility = '1.8' -} +sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' minecraft { - version = "${props.get('forge_version')}" - runDir = "run" - mappings = "${props.get('mapping')}" -} + mappings channel: 'snapshot', version: "${props.get('mapping')}" + // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable. + // accessTransformer = file('build/resources/main/META-INF/accesstransformer.cfg') + runs { + client { + workingDirectory project.file('run') -dependencies { - deobfCompile "${props.get('jei_version')}" -} + property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP' -processResources { - inputs.property "version", project.version - inputs.property "mcversion", project.minecraft.version + property 'forge.logging.console.level', 'debug' - from(sourceSets.main.resources.srcDirs) { - include 'mcmod.info' - expand 'version':project.version, 'mcversion':project.minecraft.version - } - - from(sourceSets.main.resources.srcDirs) { - exclude 'mcmod.info' + mods { + blockdrops { + source sourceSets.main + } + } + } + + server { + workingDirectory project.file('run') + + property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP' + + property 'forge.logging.console.level', 'debug' + + mods { + blockdrops { + source sourceSets.main + } + } + } } } - repositories { maven { - url "http://dvs1.progwml6.com/files/maven" + name = "Progwml6 maven" + url = "https://dvs1.progwml6.com/files/maven/" } + maven { + name = "ModMaven" + url = "https://modmaven.k-4u.nl" + } +} + +dependencies { + minecraft "net.minecraftforge:forge:${props.get('mc_version')}-${props.get('forge_version')}" + compile fg.deobf("mezz.jei:jei-${props.get('mc_version')}:${props.get('jei_version')}") } +def reobfFile = file("$buildDir/reobfJar/output.jar") +def reobfArtifact = artifacts.add('default', reobfFile) { + type 'jar' + builtBy 'reobfJar' +} +publishing { + publications { + mavenJava(MavenPublication) { + artifact reobfArtifact + } + } + repositories { + maven { + url "file:///${project.projectDir}/mcmodsrepo" + } + } +} diff --git a/gradle.properties b/gradle.properties index e9b9fd5..878bf1f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,4 @@ # Sets default memory used for gradle commands. Can be overridden by user or command line properties. # This is required to provide enough memory for the Minecraft decompilation process. org.gradle.jvmargs=-Xmx3G +org.gradle.daemon=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 30d399d8d2bf522ff5de94bf434a7cc43a9a74b5..7a3265ee94c0ab25cf079ac8ccdf87f41d455d42 100644 GIT binary patch delta 29215 zcmZ6SV{j$F)~++LZQHhO+qSJ8?IaU(V%zqPGZRnDiEZ1OJLlG|I^TD@s=KPId-bnX zZ&yF-?S@Q<)*1+8RRsu0OfWE5STHa!F|Z_L66F8Rc$1FglLWK{vQO~t+;82#f&K5z z|02M^|1GLsWPfNqZB?h$QJ&hW%Q7Js3mqz>3oE9n`yBVRU$#x@oQwBi+AcJ33)LbyHj! zqMkcC*8>0rEIy&CR<#riWH^xfqdjpFw6ZIxsJtb3*+MkGaN_IWQB$lF;7*k7pbeOD zmb1K+jFzL(<+Jp(OthHdw;d$swr1tQ&FL2CDdE$z7xeZ*%AB3q8QiuwFO!urfK*x> z^C0`x$!x40mfMoxlDW1;qsPANS*?;Evg_&AnAS!y*3{UXc~ktimD(YK4{d1Jq}|Lc z*-|lOHEMdI6%)Os`j~-BoQ@zX8m~4@`7HYcG`0(S+(~6a6S+T49qviR-m>6+=K%tg zSK0R&nZ0I5kcfZabQv33YV%wHKuW%q%#KSX7Qp9}^^D@A+r+(r9>~t2TrIg!jqWZx zNukP$qPw3FX418lPG#!%aL}pcI{s!<9Tuj784xp5nP7~c+@)vU0}C%a zT8ZP%B(ezC1)3gw7w^w_k?)rXPzv7cDDUkk-^XaDWk?Tm!3>DKKEb;1Go~W|XdQ#T@yZfil2BCpt9L*mD9 zh&NrIoalYIl(-U5bp40?L}i)cb+cOnWXI{8x0aHE4ND-xn9))+I5 zcMEsWsN0|4h^tP&BCwD0E@4EunRokGL}Mg&tw>6U8ifuVf1y6z@}Xe-3jdNZyyiiK z^}T?cDG`WJ){p%C{tet6HRa(@B-+gI7pyi~zZ3XG)L_Za6KUPp`~qsWL(*{zd^d6Q zUqtc}xsHkC(fbbu(v%J~Cx5lXr=0SK94Ni2(-AVB`ER#KV`NhoEW`Uq+fud1EP|gV zRM;}J5HD+h=0K?3vML*oK8?L#-xHfSMWiSiUub$SFB6iO0hYgC_n49F zMP@80Dy-^K2QzsCh{*=|!LZ-5sbxn`17b+sDm@OkLy2?ASc5##o|THd3Ntvj2lT*K zRMJFbXp$n^Q<5i?$zKF`uV3W8VE!wt|MXA{EQS8t+W!#BaTk6Mlz&Pq`fri*{|e== z9yp+?yNfSC%*)f(%GuM-!pzgfo!P?C%)=v1!^j0)3{waqb=bW%zt+H$>ItMNAZ}otZ#zWq+LK-Ho+;Jt(7*h$<2KPrh@k>N`?>nYpWR zc#&#buupd=Z&{ZnQ$v|A2dc5jt+EX5b@)YO$idW93lu%#1XGa@Z7++K2l&0!??zw} z%i{Ju$_7IClXVI`_7!`unUF}kPp3I_B4HY?`!Oosh0&@8((OFYeuZ*Tanc@XxKy=U z0%OE5XerNpH0}e$kQ(lf9CLF*_!qpQ6PiwsR-hvhrhHHWArUv`uTLgomPmDZ@Sjc$ zj?&2JZzu*^SA{jhWD@fmvB^V=5D~z-fWXk=G!hVfcnets4M)`9%W~r}}Dn zH*~ZKoF@mwRXnCAha*3oqqu0UHwVH@mYAqcDL+ny*8AV{2m=F8Y~&`tqe+z<0&qW~ zVq5tszzq$_2qd6a-Q3fPp(lc59`XnT`a=nP>M$t#_6FE4W;70kl~N}X#?d4)2in{^ zD=aB2m>farlToZo<~>-3B;|F;uRN|Nusqv?(LFYGsbg6~X-(&*rj$*B62&8CittrT`< zTog0Y;O+)I2@-ppN*k?i!pi0}%KFbH7ZF%+$`|EK&I?PQJWeS=^W2|Zc5g}|cJudJ zL9O~um=&PMqrj`c=RW_L-s`@{U0QNe+-NeYL_!p)3&7BFe*=lIdK<^6T@E6Qqs z5%*s@tTz^-zfL=lUoxXSo*xYnF8C{v8J+>LCjI-=VLy85*JL#bc*()LVDZT*IgL)T zE0Z)LnC-pH?0qw+&k)sE@IB{#8x7+tg>eDrN@Lr)Y?mZsnV0Zzan8Na=7ym=uchtD zo#%(CjU1PTr@Erf%ww6I=Z3XA`?-}Imxt=IJCwk{j|%r!jDh3w6_>4|zKb3#Ob^x{ zdh53-8tK=5;EyF1qbP|;L*^(?BzPoz)TGc*XKzL{mj($UW@YT&ZLKAuPTMa<5)eQO zlo4nZ6itY_T==q$EWDg zax|ycI42H9QgvzBwYjToWT+=T7>mFjGuqP?imXn#bpbo2@pSs;bEBRt-_>WkH zr<#Jn!c#{E=8B>9D$(K6{w**i+qR5Szhg$8reeIet)$pPyP9#+QW0y^vypSRkmHh_ zPf_2_|GEB~jr{KW(h__}6=q#L2dKc}F2V`8RCdUnItzI6jG6E9a46+*k}@p(<1XO> zVCEGDBq+|0x?k1rZv`KgB_~&RhE>DnfbZs`5B#{J5IC3T)x085M*tq^4f^Jdmojb> zvt;k4k8kM)SzB?-Sg{7?3mAI}5mWUz3P34_wAEqxupVrLT~b<3*G(ba;kiuBtGh8D zlzYInj3{L&Kly14wV-)$Y^5@lM6)uRb|_p~?2!|vP30&}?wer{&r=7iTDtZRJ?~Go zZq{DU_)JX*l5#B%Apj*I*u+2->tF^0IQ$PVWx;{Xkv)Q`Tga9pp$DG_oF zv$0XmY9em#OI5;2!ed7nx6aR@k1$u^tRAdJDzT9#IG8VD(hOx%o0s#_jVQ5^MJT$- z<8LyCXVGni;V7YHOxevj#BenG{B)dd-t9xle8Kyu6;*wscZz`-mH^LCc}$2$Odw21 zsDP_ENlKxSDFM=pNoLqYRr>co6{kx$Ej(d*1bNcINe1%h%XX@-Qb9Os*^T zdj@yeElqkrh*ap4G)TIHyt>=9uo=sa@pW6yv_N&peb>2B;`d`aovGh@x8y(T2;$7k zD)6b2AP0_jLQ%(^%tf0)twv^bxr=S3p}6Bo_c*7kBphMnJ>=QMv?B7Rb#^fy+-GKy z9Nue4IPd~ zd+gf6Kq)yH&{mH9w{;3s^|ZtN;O9)#)%xPe(ZCIMx>~_q!3Htxk3w8DYf3q+4NyA2t*UmM-%s_3_F9#ilg@=kgqxGyW@S9LBC=QhY3Py)$Fkj0YU1^zZ0VTgVYNeiSDVGBRW|jL z%(N~S@8fUN7)O|`JwqDvy=mV~8rj-w1pUp&6n(7vPv537k7@-qeLoq%XEzTHF_5aQ z5%C7ga+tlda}WHInJ5RR3?U+d2M8i63X%zZ0bc{hrq?GI(JK)tpPN58A+{b$TY{jgn8%OJ=(#FT(uf1?;rJ-MJShg=smK%+h`NuQ*Jg?fKsZp80g(Q^{85V``Q8sq40( z-`W}vVHhU;+;DO>kkm4G*V1hFGL|Z2Lhpz%H5knLOz+eD1N)Rtc#`2>PWh}+GX zoFyRZR>FwnGmJWkV!Irf&zkj!TI3%E%VIC&>cDdk73doNkd;w2a@U$jOd&0rM55uu zR`x=B$YuBqZ;KIl7Q=1IgR=G~Ca`@GUC6wqGylZ&6;Z@?F`lWkwfQg>7X@?+bZq8b zT&u53c`y!_aPGMEXhqxpvI6$R=epR4{K{f`Dxig}!KcAKQ1SJuJ@5;?T%OGw&d#6p z3fo_v!>rh8i?t<=4r{hOg==k-0gu&{QE0fHBM)(K({aS?BUqJWny?TcK?hM=d# zw#8}}CxuJ>4St>bEt}b`CppbXgO(LHd8IXfdp?Y=kj9Tz9)b*mGvRO$DnEsvmxmJe zZp8t4{0>*w0EeAeaf8#I1@t+$7zOzMri$f=4BOHNDV10qcvz3XiwUC@; zC1MYyaU)boIm6%$DJXXc%3my?cyx%`N8pWT*+uM&Yk7euF2r^V$n)6#h1cI647>Nu zY(V`A$ir1N%HRtV6pY*pK{6r`trd^XvJZ31EPhA^{nCxM2joh9qCL#Fu^jpRF5!z} zPj`zx^F+LOOWCj|b7PEGR$LE8^K|EG&H__GuNGTzBdm!KeLyIqdg3c0EIPp$qLjh% zDcGE-))jV;e%>Lpl|HQ8e}w$CIrh`&qdOMZjni~2Ket0R>{?H03EGj+gpxudp{6SM z1fb0vaLj#yfKO5zLi4Zm;4ER+b``aZe)$nqgg-M9u3@+RiQe~OS4jF;Vj-L+^?N_8 z=VT-^D$pYRIyhGZl@85_lnBZQp4p@r%UVB&SJqA9dOv;BBdW-sH(x5{DWK}w-P2XJ z4Dr$4X{m;}wc$2SI@PaTn?p%4Ny-W9@ovj00CjE0mfQ3you?u;8i6&&i(~M^IQbq_Wa}?Jl z^w&1YUtr(gPti@T8R=Uzijtl=)^0t{-q`WPfjX%TH3uwefVRHhLR87q5!V&s61y$( zoiL^DyD+OykOL@VDy)uUDzt*Dl?>0QjUl>%zf@SUD>5u@Rl|gs&|`)lpLBcJ7W<{o z+tCG0Q)fI8+xns|fA|n;j#@QK<#$$s+N3TI8B$*{jI$T^~PPI}gD8DBrF6e0sYkQhewg^ud}Ob0TTQ)ou!4%5 z!dj`5pZr>c`$EnU`9hX8H5%$nm|+`7uXDxE!i>&SB^y7rw}9^EB$N(}nHPb+$m786 zV}Bjs=d}z81aH_nfBrxxvS2hEm!dr{uz@0h4#{L8X^5{4AXZntep{SvqE3Uec;m%o zs5wZ5#L_;fK7mIhM0?7qwM4q^rZ=ih)rZx>e`H#d!@z&E+geh5(Rq2puQtr%`BHfb ztEwXU=|vJ)kD=qHH0XtdfZsXVacH^i4baQx6x*L}g5MN_9|19Kef78>`Ovl>3oNfq&c=&D@8)FL$Cu$&F|& z%OUpw4T{58#h2q6kK=I+}Pt3?mtF#T*)*2X43KPJ&>gs(S zsTnZgmO8qtsIXXJQ~z7NFmCKzYu&^&iUxM*PASK9Yb-5F$<-=LQHOJg- zj3FkxrV4VglEbc6uv%?rJ4+fH+of1s)+5(La&(W-%xbhTVyJvyBq7O~tW$bOn$fKO zEf+!2BE;}rj@@m;n{YS-pywTr+y)NcV$?z zE%sHE*@sl%Ss6mxnyu_C7#VguGD#)NNV&h{LTHGks6sMC*)e3_%gK%N;5e|uH&Ui+gGTYC zl=ZNTo#kwn9J1tqhucgTT7H+0`tj6Ii-RaVC$gs3T`U&iWW`F_M|#UqUn~(u6ZKFV z-fL~?5pznvN;tS?s$lNouqNRgjZknePg5sZykft~PPk8S1b8q_>%oCVxZX2HiLFZ) z1x!>~xfBkWKP9JJY{L+D z^CIlu@HI#J^%a#fUoGXatWk|qg#{$%6)$wo67Y7Gq^!S;5c&PIW_G%C+K{H*wvfms zZb^L>QtdPfZbAf5A=X7C9Zg3gZT&?wg)ys#`JOyBz9U}@Q(p`&2(nRd1rVD=1wn*4 zOLnLym>U9tXePy&n?vKWg$SrKjO4<-KSPGdlNWpn-B!0mWg4c7LZ$^+m67haaa4v0 zPP)P+?6)3}`TuMLB7DZ4WI3NbIhUiC`Db{NbhEg_B`L6{GC`1#dN1(TMjOgh@3Y`) z*#xc`w7dDD8C1adzhCX#8j`2k?nE21r#X^h!3)0t{pkTX>*{dGoO9my%%Gp|I~Z2x zjej1I-!z%=S5~`Y&p#PV2|FS;!TE%SAvic?HH(zM15QZG{?aXX_9-5J=FXAZZrFVN zjaHH3@mOKvuEDcH!^m>|X)-n0TD|O0izOQ_R2o7);88mvr9EUmH<4P|?8miqp{0GS zv~=tQBph)H!22nK4E)8qH3*Bps6Rw2L_Ao*n*b~d2DSwLg`v3xlR_J`E80a=Bg6@$ zjk@F|^KR(ebI_0O(t%$tgI3=cN^vknycUpd z<0KnPOpa%Ef&C>WC^J^f{uLpZe>^j1p~;gDo~YcEP7xg3xu^(KcLjhw-1e9DjM)>Gx@Erw11#J}AF6V0B% zz)vWV&G)VL<#*%!kD0;5Z!4tSRVxwQT8={s2Y2e7J&OHySXAhSp&1N7-*A$KhkY&g zXM3!Mexaa^^HB$^9%ZStq3VJ2!(9T+9yAyA8*3~s1a3Q$tHdJz7YIYjXBmEZi#>T?We7 zp_xeehp%KfN#gjY1^hIy+Lw?-`xY%gb9nT|4XfZ62E1_9js`twM-HoC#OC9rw)8cZ zq(J8e>h)C$@RBO{k!<;pN@9{8*S9ySgB75(=H71DcQs0M_e>BJpfTKj;{>&RyWgGs z;!od~Nb>tIl0GPmq~--KsCvhS9{ScJWR2+aGB2rK;vKr{4doJeaem6b*lyF<-JnB? z%2!RYJm@CGhfmz7-gID_BPtb{rD)(ck3YYUCqb$yxwXo*vPE=yHhEj+t@d=8C!P2N;Gmm^2Wt^WOOw}UK=v&1hzR%Q5L3+k=5 zNmt+0Xh<PZXDlw?!OFAXrDJk|o$`F0C&PnVM>+pU1YBo^W2(+MoF6)lYiA$u%-d z_GApr?t+*gGH$%s`i>V*E>r;l4qCa4iK2;yhCWejloJmKpgroqviJ~`TyvMcJBjKe zHTsp#?0zw!+vMvD&3x(Uds)&fFa>$Z7@t#8qP`m2Q;gb#BBygG-ldE`7iWbIjK}%< zTGgGuG@>aPY32L#_FJ|H1yXxyCSl|v53%+v%8k=lEqF^6cOtx=`;{Gg5vS;NUs0|~ zmzgLz0&5GnygkJs#y9cLHK0tz^l3Sbdn&Ip`6Eew-O#?=3Nv-j4W_>eraGB@-kKVV zYi(hO?IG+}ajx^`*lpZluv|6K^w1u~r}Sb}ya69)PGBE3!8uDVwbYRb7ftc~VHm$@ z&hFyGUU8^X5;&J(>^W!Lo;FhZjx~~u8%OtCIJ_KikmZKcNb5vdIs%A5IDdr5mNS}( zi60bgLif@ZvAxqi#7C{_&WOQr(xUWN2G0J4D5zl<<)tK4#O{UTYoIint}@ThuU3X+ z`9|kMX>j`%1<7usY8NxVs(-);{;O)&(B2*xuT|le*M$%1bh)dYBH6d4(dp{#cT9j*CiVgEY}b6 zn>CDVwkl_m2i;@sE=Wex{s|k1f)~-ku0#{P>yl~9N0TYlChvusbtmU&DAUc{&Dp!LNvu0WIHvxHMbExM*CYd` z5EiB4H>B4`bD7ssFGcf*O#B|aaRWnAp?EsjY z+>KC96uP|4>uPMXX2iBHdOr4UcnAV{QDXZ}h5hnDi^uFNBnu0dRdb31vidmcOrtu} zkTT{aS(pe8PHO8++|tGXs;5OZ9S6bDP(k+Y(_4CcuG$X+x3PzKEkRn^D?$p_M?&Tp zLFS7%CnP9;K1r)u@6|`}$M6nL@5gW%IQj|w4HMrMflYglIa$_{wNxf!jL* z>gr6I|ER{ZjqKqiB(TdHt^o!U6)t#gG1XMmFiowLnJ-njk|7V*^NK|5!@Q2WN3aOb z#&k^QCT}NuUrmfn<^ z(sj6XQP0W7ZQ_xpIO2+**oP?sI<^Lb-^d#eF!=XRHuWxbN}RfS1SM!F?RE$KT`dW!i3wB>`^pY#!?oyvR2QBnjQ{ z_!a11k)#og1%o$S8^9?EyyroHf7&|);2r)7<>-<@j_d#;;`}izj10!1u+$BFPmJIY zby=OD1Dwb84mQs4tD*~( zH+kZ4pl?v2qv;BcDoiu&jCjsB9a+~h$m;@eEIORlbVvotOW;6Z16!=9!zpF8L0X}> z?J1^xR5NYtu-+mxptY;CAYHKs%xVo{6r_TwxanVq_~DI z7vY17=_4d_{s6wae$H?gUbhd~FE6Ieo`m$JIU}S>DT?$GxSS?pfw|CL&o!j}>Yuy}qeGC1D0cgSZfm!18)!hU_SnRukP3BrFB#5} z)^a&zw4Qr3p}{j_^V2hL(O-v)+q#J^c1q7Md)TV`T0+Va-XJ1}+I3iO+!m?Ts_D^~ zG3nQ@G+>`hlap`I{x8b<>|Y$yUE{4PJ7*lATBwJq!+L54S_oWH$EaDcMN<+D8JztiCfN9>4AQvW+rL`Vx)m3anWbUIZlTAc*ulubczGu|U>F z3H<9>LjvSVi*_~|g#xigLgy81-U@M#y@D~E1wgyBHcYs;=_}BTpWaQN%~&AS+KK(? z74kbyM~v0%;I+|HgT<<}ANB`*2@;kgRGVT|n&LQ@;sv!IpCZtt|D~NR#D~qxhr_9? zEk1R2D6aFn@sG`FqM5;aI1{-Ogv{yC%)^IY2$!~HZWRvC!~)T$b0ea9*#1{y@hPB< z3%RLc6F(q21P9;FL6sC79-|f6d1Rw`>qU^t$a262Xb zMy%ZHuQoak zOsDg@wYS*6!9U-0UMjD7T5>iW(_xPQPq}>C0U6H$+XCBsfcNc&K4?>dFA$=^O$Q=L z>aGMkF1zB`51ZVmtm|_rT_)9@f{Hr;_hD)$wmjT|$2C;k88+acPI6yCCyIMXFvq85Xm(;dV zTkFH%8Kc}!|2~*r(b&~@S6db1(mCB>Gef{vUO)tN43n;rWV+_3sWTtukW?w+IrbZN zoa2B8WeJ_*l_j5A(F_18_0pcImRKzor2i)5S)}2GU$JI7@eW_$V&#$}?R~XsU9DC( z)*Gdw0V^&DKumt$(=A^8v6xFU)D1JkyldW!UHmj+)t-qpqL4eGtOKp4-6!Y9CsR*} zxXMeBH~Hu$*allHjWDFGGT>EbWe6kD97_zj={g;vryd;l`${}V#J$S=4bF2OY~=6% zBx08SCeltEa4I5iaj}sJNa=PJQY&EVSt!S;2D11lwx|+&&+vmBFfkY5*h?LAIU^15 zir<~-Z1UNhnxlNXvvZFt8DNE$AF-AgH=bYB1Fs466`^sQEgMV)T$x?cDXsMebvnb} zoWIwSF_VALUuuuS z2O{|Y8e4bHIuy2iTVOUAUiK7BY&ggL<(&6fW-{uHcQuGiB;fWl;n_4JK`h{VU$(^Q z3pKH(?z()*C)NaWW`07fuh{6n_l34wtiS%KkEKu)j#mKA_QGu-UJz+>#*GA$$owrk zGu;p<6>(B;tN?Vpts9TW1;hGQ7K9rN2Am zIB~jr*$~*o_m#-a;5{>P4?GM!0XNqD*oP4YiTlI~j_&?sE?sn=Vh^uTIpOJ*iq@^*!Uq`zQ)VmrvLm9-LlAC0V zh(DNy(8m|eT>fr18pPogpfh00D_UJa|eTd&b3W*?uB1J58eh>ilm zTduWD-4k=1n-RjcmF{m>JZX7!t9cp-X1!rsWn94ZIXs+?6dGVw=)QUX-PS3w)_LA` z$1lT+==E9@WFU9k3FMkspVl7g?mB3M-^11dz^3=OX5?oN1hzz3s?X_3a#XD_nrCSSVBbz^**2)QK%ltBEfB~3HBRum z=|OvEMeO{3D_e}+>mwMR>8rzHBD3dv1kYcs>rFJauQP{N|3IJa)3BpDJYLiDsfu4^ z{zs~I+feFRsxx{N1&C)e6_6WZORqn? z26rnQ>9eTWt)Y2RvxiKt zlOS~Wz#Pu=12?K2vNKXl-R+7>QUf71W~RtyZyspe07%ffy`rxf@cpKP3Dq zob2A^MLYS4GWz`$a*L!VX9y+}{syQuJj40&&ho#n!#speQA!LjuqQe&Fw+0{>{Fmg zDS%jqZ6Y+!N7GYNjZoqpwG`-M#NM2=_{pSAWTa+nqCs_Uyr`~ zK)|DmijaaN!fOfYqnjwcRCXaJU_R__7I>ggGJf2Fns(kCl{51khLg-x8{*1ld z>y6bpxni~d%$-8Fw&IDbQ%~6|x&_2I%$r@CS_o(sPHIJh%MSf-7ks*$n#(laLTzs-_HWXE+_i}78=OfpcB--QsIP5s`cB}fa zpW!0CGxO*a+NUG~q^iz6In3Gl)HO95X7Dv%>^*tnudMam4H;$#THtfBb`4$h@rok6w26)k zrkr0Rm_jg04AQBvzQ8v^`=2y1kEYOloyTP9%Z%st&)NO5R)$O{1Xp)|%tVt>T6Zp_ zNc)eoU-q4~{UQ(61-f|S%Nb~UM=c<52;%Q=(h+zf2}P(y;DI|;B_N0RO@p+eKp24><+$7V*a7@jGp4}gH)h6w= z0N7-wlFOq+d^f~{OxrvC$>|Q~3Py`s1nGqb*0I)7GoA-wast(`n<9Matu4{pX3!=`*eu&snF-_rq%1ls$oPwd<~B3ZzGqg;3worqYcemH zG8KHM2AXIx;>;7pP8&3ue$z-fPUmXie%bkIr}%FIY4znS zl@G8&)$_!C&~Yl&1F2Glw^CioWt*OL1KnDE=|3y)yn1AIS;j^)wEFU0R9_haAV9|! z3|uZnsPEO|Ci@K%;f_dXQ%nx_3~Hsc0VU%+w>+qSRxz5*u_RBR!d+N-;GRtZzD7}xGkiq4LFPf z>Pvp48`0X{$EMu3$qvZc--gu{eYARU)Suo8E4DPQQd8z=*&&_g>9k|+`m<;S|Hw=EPKOsX$3_+NB2L~06(0zCR#r;#Y+J+{p3Z%!a0!iB9q%{b}XAE zci+D1$G5+0bxhE;OnTFNbBIQsQiwn%LXOE*CuB)z2QSI@F!w81D!XQHp|tgZHWU14wAkv40AS`63v<`Kk*w4ZDK$XRGtls0lI#b^ESN zK`SWY88*#EOL$<0?jtY_NNUQ6PvBq|qaSMqVJ6X*yCh^dX-5QCBb{>cUNkUM^8tQM z60^ppML7LI{N5^1ei!?ZEc@8Z*$~<0XAvs0xcQHR(_GIIv-&b+y2_ zkuB{HmHJ#1#@X0DZB(6K;NombD2I}AKYEHQKl${9E9*+hs8jR^<`L}IJX%W?TC}Jf zWzAmYrREpf&!U?|tuMHrRd*_{OmUALR)ym(_cNM7k=s&8XL|3I4+3AE13y(?g~Uc# zUO&kc607Dkzpw%(e_~%u5&|#dyeMMY?N;gd{AE9GLOP%fW=T#l!bLP+c>?0ce;%$U z86@`76h0B1CH;_jY(F_r8I#EO+Q>NMSR7X`N$2Dq_xOS{hAp%oLi3>fjHW6pae*w4 z&rbCk$yeV4R4*k|;4LJibbfnPT!w=x8S+*}#(Rulm_t3Xa>n)s9s!$eO8+S9@?HBe z7FNu#x8=iNz^twytZV#!aqI3v9*_%5=-7AV(V^wt*I;IMHH~;n2)gw?`8m69YV4Pr z^J9$o6yX#$G0N0^y?S2SHuWsw1e7Xr;%bH;RVsCpkvfhvR+^MG)=4$lj^Pl&q5!Xe zFLaCj%dKqT%6(7NHyW6C=*d@7>U{Ar`6)l#m&{RZJ{KBDZDm^PVO2G45I*21@6@|1i@p*uvAhX} zoQvu}(^w}0Kk&Zv2j7vj+@00YfOPZdn`hOy=Nep=23YbU6OU$&KKtL@-}t(_ z2f^i%DCjis@n*!U9)bYcO47J3FQQS{3h85K#?!9t`xsioWT()7o~-bE2-qrSp-yfy zJR~)Wh)Kj>2cz-E0-t<-gOfwG;2d4ch9N7;&-C1a7r+rV9#2zAQh!gYJjWRsnlGamSXEn-V7C z?^XduNbo^|$7x8a#;c^^`UrP4a}yAy#Kmq_hmo>$HN27`&EpcBUxkCaeyt01!QRZ; z_MG9$;XuTDTW+lVOU|xu6jsbiZ}5R>zX2FoD*%it_gtQ&iO8*nx#c;EdBSp3I^TN6 z>k2rBIYmci|Im?rEA@Dhi|dNeY(s!zJzOEske5|fLw{l*#MM~}a0rI=w6J(wc`~Qw zo-RJhu38^lo5uOQn)DT1W}1t+Jua8wRJsce9e6m8_kh%erzzeV<{T0jsB0kIK2J33 z2>sBLay3xf$(hm@-3}IrQ1ht^;91(~IXfGSKS3}%SPQj_r0eniQQn@8G8<>T&HlGE z`oXi2x_!2wCBs@f!sFfUh#gq;_o@U~4}1IuUvy9#C?isRQnAW4>#lqbX2%udqSwfZ z3q%(5Ct=Ezlo<>e*x6%1S)k(r?Dhm@pZ|=!8yp{k`t&?YZzMOm2VEfF^_94Ur3KLw zb5Gc3hdqHuZOUWVG88r;G57p9n)S|B!Rc2OIPtXq&PVRajMo~%e`0<{nbLKl-UT_! z0oIynF@Q|#%ci}JKyf$+XidE;d8eb52ii>fF$sZDkeN50iTS~*D;65HCO!?;LZz0v zScH>s7C*2C!dM%@)x+Fi|MH*;%;$*9;h+(UH&R_QUSU$fU)MM$gi%rEePwxT*Tao zt~(U2vD9nSC|%TV*5X@FSRnFH0}$rP#nqfNSQx9x7YcGrhEnjfHdGXki{ONg=Jss0z;ln%psJ!Mu4XaESHAWS^1Qq0t0&wQ$5r95?;@v;aQEc3LApVdZAW73_`rq~ zSBPt2f%b-&y%g^aWnPwqf%h@^7>Oq0yERDS#=6oAA;5znZPp3)e4Xs34(Q1}gb#m% zUikTqMz%tgPe?(gF$mxFEBAXB-p%RrZ92n%6=to{I~%P1UT<#P^G3eJ$!mzk8p+-@ zz^`Aa;zPLOpy;m!=^5ie%=hn_i~7cm?!nW>6I0(kl^u5Qs+}IOYD3TA&vtlU8v3k) z@i#s|_^(Ly0?*3)|9-mRl>)g~wJrJqpAUR)oW!4tiz*J!b7x}q&P1hNb?m@Tusjh_ z0!ocm)BX{Bsmb=LHtH90tnjqZDdxq&{`8==W+;FZ`T*3_yKVJ^H-qv0<}yOgx9)kz zqjedMjQ-Hr8+X($9P;94ip1adZU<;MjyP&)YyPI6{$@?@O{apvctDgnbICxYqXzNG zcCrC~)MFirz6Ke^YpEf@DB2Yiew4wj#Jx{9-8WQo3shG@aMC`MoloRO^lfj5T%o8D zoKq^@PkToZmIa2Lj!5gdnqYI(4Mx9s6rjkqwdsm06$P0RM%Mr%}8a zprVMEph(u%z_n3A1kh!E9l6!xfSsY&&3QQESCC(S!l37^Md`DKfd)X#Uqs5CxX|$L zx9t$c6D(fA1Tc}AIn6>Rd{qj3MK8KiKn-X9me&BijPt}o0C8Aq=+xn%vY_U!S?yz8 zu2zWBPAWT$F;)xqHxr1145S0)@kgSS%M{)I#qdG#j@!TJh`t30kVJrJ+#L2UPx*qtGe;H&B4)Oj^T~`4W=gzc&;_gmyFYaF4 zio3hJJ6oiSOIh6Aiff@bMT-`vI26}H@xs6DCl~Jh{+xZ!dEb+HCKH)#GD#+GOeIDF z+r#faJX}=G!75@x^Ps4P9eQOk*R@g!Wh7)PwCVS-_ABBYPYRUEaF)f#;k)mZ@G9TV zl2JpP9nWHzy>Q=y(4m9q3EYzdzIXvJM_N+5v(7}iSJ&pNJJ}uWaZhoF&1!Z&9Tc^0 zAR$WqPUZ2<2N;i5#N3L?HR(~%H0Z^4`%)ov#l%_|jbHQim-=0C^O3mMdu9{2Le#rx zxfg!ZCXevCIk{CgSabow{} zoiDnl=haU#5|Wl`CFCB(!@V(LC+?i%u}^hN2o=t#cWu$#D-Cfb$ipToOkr+&=Zdgn zqCIW^$IY@*%(-Keg}n(WX2{;=tRaoMKRn4+=K9>x=(d^TD~m`CyYX0 zTdx8@v+tKCl+OJgOPf{~4CHgeX>LSMM*Y%x&a+NNGMXPB#t$B$oKYX6*9D`+6RQT4 zyQHEM=zCEJvb9taj}&yY8RXbL1B?e1bqewFYb-oXmDh(oC4m_Xj(yI=dTjPz06V9( zbiyxtX-|jnh2P;x-Zn&z58f}}D{i{6`y2K-;24E(M6pqws$v^qerNe|ioqA+v7vLPA=pJ*7CJe zi6K}0W!IFSrhUrJ=8l1v5oHm5w`G(tdgFL8)B`ouiQJOCJZ8!4aT$^!G!${^$$N&X z6WPT2n&|0t-^)DR7aO9}h5_5@CM^4QlH&JB*A~m%79{q-B@|HUU5UIW`R1qLrMjQ4 z3pE^Wt)JNXOS#KzrY38*0_Qf4th=4B&TG@{Ee_}bmJM^GVtuocUhbz%?Wk=L&g=F8 z)i^LrqYdt*WUa*l{n5-4Qszg)+%@jfWF=F{Oq;+R;6zBtK z>b-C-Dt(NEG~l(Rxzh$<3F`;)C>z7A_GGSmcrQ*txGb$!fXH|wAb_m zL)O=66;fwzD4pngDIW{g?ggryYtI;QI%io!bXNA4wkR0_3)TV6*IALPcGoW+O4pUH z>w}rm=4nPP`bWiPU1NfCZ=>j!iJrMu5?(=;ch=Ci7`t6FRhZ|c4B)#aPM#F{11=exqNOp*FoPempt#@KSs5&xhK1FduP%TVu7_qMIz z%JfWsJK=#`Q^mgJcfRTb%8lV+M*KiMFMc}uJ!MB5@{b`_8HA2V;$I%JOyYj9!6K3N zYJbse>#S`4QPbRQ=U1lUeEWDo&`E3Fer2DfJw`%&6BJVpsK0?mc%Qx2T&?V?OsHL` zI(EP;6bhQ_hcTxjg$Spre;A7Z@6V}It-SR%vvTQWUQS4vjA{>w@dK^9bmX~h*`hmND5(2V0EqG zo^51&wA)V!_y_@WvQv&CTP0Fsm9-HU5bv7kXCFmOy1Cz;i8HpYjxziqd!IU%ZR|Cv zm^V6Ue&LPWHETa_RM1&h*XA_#aw@(|k&(Dw?=VIWlt__Ss1N+F>>^Nyr?D(m5H%SM zE5ZCdi%?mnQ^{A2hB@@%BA_k&cp-3#OM)5bnO8LkFq$MpJ)&q*6^GN(#R=P#=5-I_ zRd;Csjzz8CwGLeF^MqpqEtNv30w*xX(2{x4d!MF}IX+pf<{H9)j0>yq2ZAbkaaON)f@)~GeLz<09s*qO?xC3S7xS-#6}3|W%mgu^(iry*Gg~l4+rqRS8rZyrSu;OrR$Jeg7ywS zU}08HPBSx!BXU~9;9TA3g*YUfgg)`MV^ZX=5Z>~`m3uqWHb;GyyGQ1jc?4SBAgeu8 z1uVt55KF_E9h=vIPY=BcKzI>{Sb#x(UezRd^ z5Fn?tY=@R@LTvBC8>CTf%4tY(#-g+CCA;=WosA_$Gj(Q;6H-l<-Wt`ExyG7`6sn){ z)*JvZZiw3f);)!IDtyc`wRC+7r0TC^y(=)jc$Z)>YA0mCJTMI&iBmzxvVZc8 zm886dN6;-oFK4D5R8?13tiOeB7E=-O23&2dg7)M$g zb731x{FUGG<{kSdn{geYum&bL>eFSLN3j>NTGjS&cuDCB?X>d< zD2u}25T`243dPQK(+}E!LuR{+$!|N6iP)xeO4caeu-1-I0?bW(X{$;|dfE5fmZ!V` z1Tytl3rx%I!+g5htJ9mPDjxlkL9q$HC}SP%VyQtQNy5WAjtb&Fi%>C=pPB2E-)O$< zO=0HyxrcehMr5)_x9P|8i*%>pJ0vam*p)*||63F`N)ubfSE4P4+)Q;4o#!cU# z@az(GLAsRkkKuUGx^Y)&Gwia*MPyZ`fEW6!Dp3eQRVC`WQf(h7f(jAE`yH!QT!W4` zKd00co7wMCk5vt;DOko#109j{Q=1&V94{;GTRIJY<=#CIs?s|#*ml+EqdAs+uUZSE zMq^$kvq=fl6p2g>ql0sm_hG&tl|^bhJU2Jk0EcuF3nyoT11D!QD3ay0D7aeP9}wdN z4Xd=G3dNL^qWzkwD8|7NyPEOaJ@L@PGHXUa8Km0s=NIf3hOj+?x-|E%2 zjIUOx-CKyVI87MHtz2ZtrD}Ymz6vmbZ%!i}Rul2H&}G1~5;?PI${HzOr@8^oP^2_| zP};K;gVG*`zvBlPjAPGv!BTMPxKeLLF#kfklTPDGYKPSyvmi>ZS^ZA^2oRs}- zN|Z0@08-{m_jq+Y+Kcbhx?;pI(&KBwpHF;bL@WYUcBS8{GC{Du6^%u~?_mX~=)dX+ z$&guV7N4#>>gD1|4Y`x{2EHWi5f}e;PoBsnbtx4l?P|R?*9_;e#Dvp%H}ulCAWV4< zbyDJ@tZAEA7eAjXtm%C~g9yP`-X=sA)a3gYnYiAe2kNcR{mX$~s8);jeC{GWr;}SP z#J>GPfoP366MS1&TLxBpLS1|SJk6^IK73zfyqjI=_y{-`K9O5CpbO_$N;FbQf$uur zE~ukhwModgLq?=J2Rl~b4bXhCDnztQCYbe+81oInWCdshKj9usZeXg{w4%3V^-UXu z-7#^tm`yIwz0wxN-LV#=eI42&Q%>3GXPiSArT3^C8)989Hmvl!?Owg(BwclzW0hxB zd8OFxm9*IC4{20#DP`z)ZuEStKZ0T?^&Wh<;s!jR|CjeUMN}dH$UzB96n)_F+sQn|edgh) z=Qz*#b)jeP7PGB7I0bJgP0H7Z6JWg}=lap@p(dZ@`$ zV-RLi<6<4ijxvA8hP0Z65DvC&tod`)J zS$`}I7oR%v^_xY?I0;m5T+?|VM8QFxG&FK#uIAeRq&IrIef%MY#{(r~kOMh0ko4m0%Bp5b2soZ=9IV7Okuq z1*>8iE!c~rkCTGG<-=44klsF6s7XZ6i~?D0xaebT0I2t4&m048q0`(F^;5IRMn|1{ zoTsU2PGyi*hn-1yu*qBq%vBNCMK^K4Z-r8X?P&0itoIvwa`A=8>;=#*m9VnIWNw9N zCofdd$v|@P707kD&@Bb9L&Icovgd_pGAeK1(2J_F54usyXrLq{h$hR{e}yZgdTlEg z-+-*jsujFz3x26nOrHyQJq%^eKfUw|>3PFlderFC^NXm!`GB*=X(Q%a zxM#4~K;Ew8JJ4V}`4K@VPvPWF_%S{(%jZ*trfJ^A`Oei!TiR8|=IDrC`;QzvBiI^} zN7VIN^p|LxqwM>q67WjH@Ee)P2uf#wF+DMFJA5ak^&|G|egSjNH3EVt7v@l}VeLpa z6sjBMmn);ae%EV$KHj8mtEByK-ooPoq+2s#J>JPXXy>QzS+h59pz3pWW~6(s@yAJb z*0D_d6Lyr&PWRA-vHGnR-j3H^?^qmu#+&~f)pko!-GSYovT$Er)1@VTO*n1@u)5?R zZzD59+fEIT?dLgFfNg)ZiMVZ%=)x84Bx}vLl=1TA{t+R;F$pm~DZxIG5(-MOFAykA zEpe+@UNSS5nVz%%Mz1(Kho8(uw)s7auI&nc@wQxg=q$tAD)GISXkuI(9@f{TVJ)qZ zX~MqN{sYHCn|tpA5kB<$s%`GA1G@9FbtTYBU=^8Dr9?TH3>f9KZ88k&F7&O2mub(5 z7*(Y&h!<9^<@cp%r>MZ28GnFo6GNE-fP6E27Uc_>vp)UmNbXhVABDV-(h|yaTi=(6 zD}aEqzKXU42Q`jgtyAkUJ^RYLPeSM2G`}S7;s?RB#=O#LYET-tdr_2z02n3Kq@ACY z$$78hHlVI%wYXs3Nz#t3R@V#pW`HG-7!f$!t`j3>Gyk0hNeseJtL9Dr&2VGyi|kW$2lGL9Nzfeov~(lA^)9^W}gYIqFai=@)}vVS2JIf}5qNKih6O)b)}#Yqq8bb9N|{|B6iaVL4;1ShLVCc+hw8 zE=_E{AQ-J=MUWjRwuS)+^2_#1@HoA4jI~6eI0xd)jH&L~7p0dOqbNFyBd!Y|c0`@j z_ME7FYx%OTa!S~y*^SYr+pTiJ;GH^3(w7=cx)xSQdq6+K+*nHgF_s7sk?b+>b9y0@zlF>1|Z5klI48@ zfr4;ALh!spqN@%DoulF;hcFvg3<1VeC2^S%J-(F@_*idew-e5NtX3x+_3Br9jk}TB z?h@>gjp3jN65yopIT1i|p@_UW$)zd5VhrG3%>y%LO z7gFj}{uj}X3eu#2Nz0=zEVvEP!ew>~rP)^=dzR_Sh81Uo$=RcN`#MVg=}T3Dy9>)} zcM=*&LR_7aY%dQap>SdNG?Rc$bg~l#yal=s0bSqtj@o`Y(T@fiL{GCY%?iK#Vm5Pv zE6+6}$d;+04z5TI1xb;0UHGw1J&U8|8Lm3VY^)F7ddY79CH)t{m5R*12pMJ}0>l~N zt1XSFN*VnG4~iHn)?}&fmb#nA9igl6w-keVo*`}`&r3|q!Lx^KiIeLmZCnKnxI?KSk;tNHUsE}&XE!HHYmY|JeLE}mNwFiST6juhpO_a z{T;7Nze z(wCBADc6Q%{Uwv-{vsy}`EtN6?GR*stBMk1t_9@RU8sGj&G(WP(75QtOa&d$6Pa&` z3MX-wLa}2a1K_K%CQ$HQAxXt#P7}_2LURW8VK}3u09#%1+Jdcg*&T>Q(bRL=I!j8@ zl(>`lw9(Lu6YG;n?n@9Nu}#c-WJ564R?LP{lgp@0GNImEcqUbOggvH@)zZCqKrtExZx= zg0_q4^dza!0_TrPi#>gk(If0!2~tTWpAfa+uYh&-^zBJe0X=%A?Mc3r(j`p^iO!6` zj+d|ivT^0DuP&%7&Hl$C%pn1_*2#eq)lO~7!<9j( zU>Cp*ZdsAU!ksc*wkFNr#&f1g73FOgAzf=sA&nS@eQ`^SzX?`;Per3+aTU({;wrhT zGSgsX$MmE;fbV!#`hqA25oy!86~JLs=+K4rke5!hx6rL)ijQ>byfLgqFf8PFo&Jpg z+tB+-(TL~%V zyL5g8B+K2ZME03&b#H1b=*$CYBOViJV~?tse*a>b+W2EQC}Jx^uXDp^D~Q^$8%2|C zv6i+=s7A7XJ1M*jLs}??2i=zMC+53El=v41OrVkCm&5 zxxEFGmn&$$oA4DQ-SD`a0yBM+0*l<6A=w#)v7s#nhEc|$5rsyW=avfy5QRSV6Kj9H zihj^jbN{+eKkFN?U1JA)(pmsys80wg%%=rt5rM56itHxFsjd-)IC(hn)1if*k%}5+ zClghPQ1BFBZ0ruH2zn;WX~y2L-Xp9UeDN~+mZG9Bua)zQfOK^S?1gHkkg=E3;+K8E_XgO8h%Ui81*PguIBJ{jQS|e7>3z;%=Lg><7S!c%xKt{vBiy^|6pE~! z(xeo)926QgV{A1h<(Cf2^GGq(xIoB&O^crryVmuVcEB-Kj0j7#L~&{rl?zu2tBiC* znn~HXU4t3ThDcr0)Z?=ehIHL88_JUtR}HSqRD#@P1sWu`hplQxUNQ6uWWr1 zxOJ<>5*#TgZEEM=K=zWL5zd*Hi8C&|ky@T*i0~*%j?T_6bL9$&>|$3hEZLs~G^v(- zu!~7A(@jRApJ&VvcF1O5HenElQLzt!qvQ^$^_AF^93Y!n)Y!(`;cSgs#@i|NboHt7 ziYjhn1sg<Z;})JpkTE{+D~)zKS(hcd%Qwj<~4P`B~AvI><$j z`*nuxN&I+*Jm7V9N8A}d!fF*jMW>?SR+D*Kge+Uq^uS!FaW zSq$Gp;oE$$C*%Ccd5}tf2fRW*WOiifV5wM&Nv@RFeN*(wYXt-XTzdi!;ve7rfJg>s z!{MeuRr4*|nD2fOrd`T;$rO#cwwLdF-VL*lwAp4(2*wEh(4<`)H|OgGTn~9CUEv%+ z<8mGP7qawH7S3>~rBFU%Nhpc17T(q#P1^Z=T+E?TK=pO_xS8++J=(p=TeIH#ON_f4 zK5XM`YGKQ4(FB`th#~W+`tT}nv1a+|P~6vTpLz0Em0fddDR?$r+cJQ@pA?ClbK{}f zNpNO+#iszQ;P0F^j|)ox3|;u|04dt9dtL>+)`xn;>h_+Sy2VvNSS;hg(7uK_||Zz7Vx`*VK0v$xCH6c}m?xk;WRl)ba*Bt*JLU9AFi2Z=N@PS9)- zL(gua!p?3=M54la8Tz7#<$6TERh&9u@7a1!nQEhJN8zkwa%)8e;E}_k6I+Z%Lrd6L zd89ab(XeB-R2@PN_mvGuz+FWmZh4Ji=l-$l6skk7rx2akg|(F!9wxBIJ9DtsEc&kY zoTE7WM;H`pFi4D!Owi?2>R?i&vp?rOTxZlRVi9&?46B9d`?^!|@8k;Co(KzOJ$;ng z8&5v#e>FwFc=}zOx&L%Uvp%khANq+WiLDgQB7U$HWER}d zNr7tps6c^X3;-2$A+H;4ZM>)k$;z@y93~@Ad86nc^rX+`Sf&crw}C-OE0zlmo+){XQ@{6X2=?Tun z*q?SXl@>7$`Ru+c)1=`%s_i`+sx6dB<~?0ZSkg6bhFWSUoV^eHzUe?kQXGp(tK`t)Z3!Nb zBd*ROL}7VOUI2x(c-7v|A&oXpW!VooMg zDk5T@Mf_|XE9Z-1OR0((AIp*QHV5niIhZACrGl4lmU;uIepHukE9#%~VP3p&omu25 z+`o|Sl=8hy>32KF^oieJPThjhVbUDc{J=a08TFv*9WLaX-4z%L2n`t&?$0WTW+QS#3lefCX7dp=&Mg*G`-3*t|QBu=Uo@VgmB!lbsEi% z^LIci7h%lEZmTa=gNGz=$p?wc+qLTiK?iJfI0J0$bq?1ZvfX_Nn$1ix zH443aTPDKz*-pMU8NzftWh>is6L&pKsABR$FY`19OTHD$BTt>d&Ye=X+D!pAG#B>Y zyO>%xt#dZ$uIVU>qj^B5(Yw=Kp~jB*=6cCbM}{iEHk{R3(C@V|^`uAw?+qr9e{p?S z#}|#7tGula)eW++14OnH7)3F;GR-X|Yw@h=wB2kPtmA#OgCRg>^nS;LP)#sv*qJRggq!|h+cab3Kr7vy2;IglMKN|*XN&4i)KmN>h3j;pRc+bi=G?Of|mt<<(KQ5U#Gg(!siUb*l=al|e<8=U+$E%BKEp|H~!cu2~N z!%Z>IcPjZw=x^DU;Ecp=Rm|^`GO73UvjoI9pf+edW4F9648#_*PB0z9@nl>&I(f;n zMTOgAxx=Ap*(fHmpz}yPs4s{mzfZpqw705@l$LP$QsNygTbGp=rGPjk< zki1i7e9XYqIQzj$S<$KGjVV8J@_MKLmU)&Uc{$pFLCzVvbCwO}_-W>aAWJOEYlocr zkJ3AwGp}MObiRi(t5#skMoxzl2e?Lh2Q4}MO(P2Z&Y$(>={XBeH30vY!xjLn0U9vi z<+MKcIdF%m6%aWOyry(MhCo54!Mqp!JZZ|26TdR2G_XLJc;^W}=VP6TJmGYXe`1$oZ%Qu6r3av` z+4{Xp2cQ%C1j_JU-iFn>R!$su*H(mmH&n{A4ekm*4H(yAIAA{K`GVX6?wF}A)q8PF z-j3;({a#w1hCP|dQ6ql_q9sP_2ED7M#lpy=y zNVfd?-M;!IYWFW;cmo%j2!uic)j1yy`}>`ddq8r<)GOdwMr$AIw?`UlX5u~Wo`T(? z3Fpy^sBU&4uBhz|T%k8F^m&piOzP|tIN8Hl^n(Fp@61AHA&<7`3uTl%AwI^q?5^iu zx`(V%N(CC1cn77P;TE5vO4<9JY1u6gW_PEvj$!N^9kytWqw_507`K_GgySIyO>{5_ zDgXe8rz*(jZ00w-+t<)P{4uG8-)Uq=z*l-idLr03V5(sZkt~tNV6|wNJE!bgyzdqI z8a<7QG42`RG@%&1H6eC}G$MJ+%sT(@WiO1sPjxYSk7j{t=u@Q`Pc!o}q&{2BCZwf} z2rvvoI86F|W-tB_GI$4?abkl_f(*)62OLagiEV+uwd3Ljg=al^yesFkVaHuW9_G`Y zKQCYm?&&vo$^A3D&%5DgdNEZu1c~*R3jnmoUM=t!1STl6m4@}dbG+|=pV`6x`yL}O z>3;)01c&gdPH-SgASRgZcaHxGd%sr^Uj03#XV)|RMGSr; zh6GJ^aw0v+2S~ULA1uEnEI0xO1iqaFe*4{ck^|e=mf;{D4A0a6DtRfd7Zpay#|2f>3Gy zT@c})6#1~WYW{9=;3iD^7XbVgAoBR-D|ARt(ufuz*`fYLfBo+k2aefJqX zjm{r>;f`mO^K4n|89kK#ANqf-vSB?<2v&}w4T|no&o5r!2klVDU0f#l}7&g zccqQmgOmn{L2DLxAm`68PxDI)ErUz|2{yTbMFAg1e-g|b|6p43(f-M`z3(^IUZ+14 z*B;0q=b>j@*^MmXiov{g!4KUNo)8de9$*Y52*n8Q9~qQtg1-`a0d_tN?wAFhf!=w7 z;=YJI7260+_-ny$k>UQX#6-SeS~`%(Am&qg@k!k6eK2o-a0e^+j5k`qAA!26i2fna zvzhcWkXjHJr0`6!-~K7+vyeaBIzN*?i+t1jyS%WX|3pUhl0A!jJM=peC+<(Aaz8An zZ;0rr?9b*<&%}UF_%DXF59b+EjQ-zDag+W{@Vpb!AN(Uu%74(jYVNK!j#mG=Z0hfN z|7-yJEcwf{|1bdE^*$4s3CB7Z4qQE-z%sBu!%Sp?SibN*6IJ<7W1`LZ!z`|k@@d}R z6*b^lf&bq68NmCqpE0t?2h&rq|8JS~yd6^$!A)ZTJO+3ULvI9C2hjbaszF(8IBj5o z$-vt9>RCFwZD0Te=zlcwC{ZqZf3WHxf!h`TGt6-(Xs2KFStcune;0XAH)y?1;2){~ zcPs?v^1MGz>ILPsf>&GrJs$bbVCd-(2kbEPN!acM4~>YiVZih4-$l<3VFWG$k+Yh*{7=(x>i^J>gx5h zTB^b8Gr*CQ<-j4}KtNz%KnN7hB@>Z|k^YAp0&Ck&pfX#6UG~ zS2uHeWfw;$a~C&jbJzcFUfv?^ZdT?FZq}y8ZjLUDrgp}zuBB@Bj%Xq%0a$>D@x{YR zquSKXH6QhpD91%3JlNPsSQdpU1$iNRV{pqlPcz5F$NJ}Jd-ezD_u`ml;Zzb(^t<7? zYyPJhe!lLvqqFrpkSQ-4ve;l)AUq_vEzLG(V_Yl-r<(TKEg`yIhb_&{5bFTy0Y z!G7hh@D4S8GwGF~G9&FO-ypAL0UoSJ2BB9Pll5V!H{{Oe=Ku`(iZ*PP8scYiO2t?6 z4OCTcGP1iC!<(u$n3VN>+7yJfD-x8Z(1`Lw;v?sM2ya*vog(39yJGGHP* z`w3nXCrwZEMy=BTkZqcQ4TXTO*!U9!Ja!;S;S;Dd*%t(t?qgGv>nwss=i4y`%*dEA zBM;Tt{npAOQaS~rwWh&h)h@wvHDjS~OTA3t{hYWXBSqwIP!5*!yoc`Cw=o+R{xc-T zzeGrc?={r$1spb?_}1hBg4WFCfQWerKYK5@MEK(@eddMld?M&6`qN9uFj;9t0V8no z6lDHN*?6!@lBsLky6U7RCI1dp(&~B+3AyK3nP7?cFL;6p7fLc>?-Ccnhq*%j`64(I$a=~ zmPfjn)aVCj#|>J83<+PfzJ;k70=k4sfXFf%$7vHq+u z{cm$kreoOuKdi4gjW`z!gIBmnkO1A*96cV$2lJi&BPHeVYd2TT)IfFLz7_OAmcnI%N z(8JIuE>j*36Vb?Cdv;nMA<_rYzq7l4pPWKJU#o~9$VbT}ZH3KXs3jvyCsLj$1_W_*9NhRLY+jtwYV(lJNb_HX5X=A! zHc+#$EIe_BaN5LSCltInEfa_?;^F|DfDE*?k~nJKd3Or6n=>CJcQ=YJ%23s_R2IrI zI!VSgTfMAA+eGD5=Gfp`9FP1WYzD;bE{3b z1#y}(kpdO|CxsWfMOqXG&8%{Fba6;kswR}X#ycBZ)NZ+UR!91817GdpRPCu2m&Tk@ zGj|S72bzx_o6$`svu6_#d^JAyU-r{xE24#REGaoQs1-`iFld`a4g^`a=q?PmwN|!1 z^rk?(%p&v3a6&mi7bAZg`%Lub`@T1a>2gLssh&snEKWRXvV(;r#Y$%q6ryD~=CJZh z@KKO)%y0S1rvnx?c;nv-DPXfJ|By*wK@a9Rn#NRPYIrW8u97(C|J>+|pbyARoNm*L zWIHjX)c(r&7x~u?kw~A=*p_$4CF4_MO>7ppg@+&NzgT^OFv_UAa%ZD3o^J7S@4q_g zzBHgQw5eilqO5RkAtIo&qOO-u^6tAv5UwZJmL1P6A7nDj592CrT}#GB$7H<2Dv6_A zuhYU>%DPs+?d}`a6a#oc7aY^>sM&Q-lP zq5q~XK7BxNj2j{2#}S6GQ>C2N}^ z#OR25r9=nwl$n>s@Dn25{Ch{UNZPKHUiD&iVV2sqizJF`?Y2>D95g~+-lWxjWN?7>X;wtueQ`X_!P>R$2G{F_%1&T83k4DSptrSl&HxS zmy`XmeITp(wk=*cDThNRoD$G~kt$goURDFPlj2b9 zaZV^X@;u9RF~*M9<$GZ|=p-BX-M}qP%KQKqGony@5RH0huzr~`4`OF`Z~9Q5e#&n6f{$7-x$+Drn=xa!Yo?_t`%Hu=GbNj*LAKazyL^8cF8OCc{^`R zTaWD9sa$IJh{vhl$47q6K3m{X0724{m=`ulV+R-JO;t48Y}B^B?Mmp5c8F!BgfOo!J$(m+Ma)FhqFv)83rUliB~;BW z4dA*lAkU@R1Ph(<5CY^cednqr{`zpdL`6vC4IWRBV%r-D=?K-7KSU8|UDN_IEGFD5 z0N*4}4;&=2Qw4>Mzetgbk1vtft@r)?2XT1-)h~1ElFct-QL2j z*ea32rDY+x=-_`P{@*`=X?;+a(SJw-1uD6K7&m!h3J(~YwB@iYj1q2{%&kbsHCM>M za3)+Br*{d}0ivRj!bwmftRkuxnLQ;Fb49z6coR6H79<`z0*O2zMryIHm`#r#2|qtG zHz)MueZ2<^_yG&#tY+;_d3%xwkwud>__pWwLH(BAILnIT(*h77ft(R@9 z2w~lNLez=t4}qTbtUAkn+<@ziLB8r`OSZptt3pj+>3PX!RJ`tDV6A1u+-2z6y?yop zNHv24NHa$pcOSdZ>$p%@qr&vFjm6)UT=bV5KS09P1SZ;xWsa2Aq1Grvd*f{fAHq~+ z^`44)xH4@xkHXY7G1aEri5Ehhi`tjO9Yc6;UBOfP4Uiz0V+NS6^m z%BP)Mv^w?)yNOjkas!l>;p8_`5K_u3}3or3cRG}P0#i0b2c%u+UGjSfA#PYpkSAmq zXJBr^^B>^FldEtxdeg$^w)f0^{v+3Q{(A4<-w*7-?{}SH#+V8(F)?&W_MDN5C@zk7 z%BK`4Ow&hZMo)BFDvcbV=0jSzW(PyNM&DsU#p!Tth0R_YdIdvXO`=}P zXLADQAS2EZ7Szf#EkzxHokVt))h40ygxLsieJH^lmL0XS(4~o5sX3rp9@6o+-MRjq zqP_o}Cp~7wT`3CG)OKi1pUj*lyHMRV>nkvl3ky-v;tsc!+j{9daWh{rQ@5-sD6MIn z2sKBwL0xdc479IBsRyB9o5W>wY{fHo>7s~|d#Va%Jk7&$FZkRn^l7-{k-BjDH6*VS zuIF@a>h)q$_oqCq0bWrxW&hGgQtm}gjj&tfoU3-*b!h@v?wuY*$IEK8Y-b&s9vo-c zlJsnK(U7?8tnsM%+}mA>4D4_cN2QHJOpQHXPVY@ zFY7++8gud+3EEpmpAb?%8FZFzBf~KKzyw$#v{`E+GEOGA;7mwd^)5X=-cfjnPM9C8 z;}<^fPA~w(rtMpCR&(haTBq$>d|>EXeh>&yc9t8Df0q$)yFtzjT6ygU>qV18!fxKa zAYjDS&e^O>0GFbwH`{9+dfY-xpRCM*rsdS zWlHywaTFnvRqW>|@V)l<$s7D7oc9XxtB~Q#e!2(hP?mQyB#$5l;Xvm6nZwWejZs5%lxi}G^UgYA<&exCS*KP zf^Kk}OL2cKC+HsF*&xET*A9zq#y`KuIMGi9a1l(rJ)jxWIJk@#41MgT-j&0<&)8~=S7)Lj33jNvLXkjjwW{F{ zL>77c(H32P7GsZ>h`#>@1I~X_zE8r$Fp1=ro$ZS|8lqmk*YAV~o+oD~+)WCEQl}Lt zF%_n6UTtw=G$t}~VlJ+|p=9T`*n5Y*M(zU1+5{ydf<+7`-f*@swg+K5;S*2p;6I|z z=}o?jNH73JS4gj+V0~nH`08RdsC>6Yz0@VIvbDMDelbpLb9-NJD)avA=!yR4mSEpr zi+W<*p&~8HWFi@&d>2S%9q9NdV^o(Bzqc7>-%#U-z?SA;P<*U0C313}j`hi^)-Qk$ zR6=bI&f7aK9ZIv%+}A<8&>zMtiqnF-SW?(GCfhrb)C_M=%U4taL z#DCEL+ZUF4Gk0k=CrF2aq5n@nHs^8`g$WG;;*iXuzzj6S*FybSTf_8D=8kgRXbmx; zj-~&DC8GwALBC%WudV;2Rov{bwIz3?z3b|(9tn3r2fX=1fTesvVe8V2w15U2$Ak6# z`K8xJyZj2>%XMeHJHy4cm)-vFXa1WDG-s^;kUMIKC}&ia0Lo#6rk6xaCHw>+|EJBY z5p6Wuc?77EK&VGS4W)IeN}#fxa-+vuBiV>GF5*WK7e58Gd{9Lojy^nc_5;h0IZ$)< z!^=+`wUp>096dAp@go?&Z$dEqeHg9cM-$F8z8F?Na<8HClZ9Z2oxO*0kKcj`EP zpNrFnKmK;_Kj=&yPCRli#^X&L)eVOfqaCg5JOOml-_izjhMUtWyOvdMg-FagyGacu z$|Zk`kq=+=IBoq=Q^fj^OF&Fhi9xT++Erg{=e0_|7Bg%n+c`AK(}i@R zx}(zDgWl<`BP&S%3BY+=b-}Qf%kyZGeTVIz#T%fSP*1}t93Ori5llX)>Ps;24r+d z^isWH{Ss4ECdz4&eJx_lHv2?EdYOd9#CB2{_2wrUjaNrF z8mG-0=D>9I(j8U9Ig;3Q&>2;_kBWk%f0SJ>!U5op_Bd3(SDybx5VDkPq75LehJP=O zGK_q-?J3%y>9*|Hcvrya*I)@M2kLYsE51_5t?Ns$^f`$cx)s0YM(rIT_q-R2#Y;X& zVFHKpHENDDFoA=Hm@QsXX%~!qMi?$-)z}a(wna{$_3(#H!6EJ$1qHQTd^)VZOm+5N zEx!shXit;NPN>qQw@ZxcQnpK!as?ZI8A}4ThxgRC$HSxgQ1EHHeHT{V$DC0Cc}>`Q1HZJ zz@5IH=JEBXhTg5}Ew$zlU{W@Ke4QaXRP8>9H_?Ew^2F&|@L}PfwMD?uq<8!&nJIs- zucYTqILifVfEYq)S1F~WES>jkX{XcWWuQ3TB=jy>Wlp~1UHhAyXFAds`^k+1Q#rDv7jL83YsowmV~*UxbUh}|4>K@>l5zR zg5_qnjddjYNQwps;0Uw*2j@RIC6&4MQeQSq@wqTgl-%Tg6U^&B z!hUtZRCOz?uT-zjR*233Qy}E**-&;Dw8uG>$GMNH(u=YgIP}?4?ZKKfB*T^7K?~^+ z%cyc!Be6AsaT@euh=zrhX!mut=2$0LY134GmgGe$ zBbfQ-dK6F!!||~VIoUA?6wQe*jcjO)5yK=Pk|Tia4=Q$z&8+NHzFQG!sasFEWta09 z6H9$naxCOMB~|8^x#v)33vx!*2rBYB@jkuVNd4GrT4&|W_X`t?St85iKNN1 zGO;LQ5u^W=V)+n*t+0pnDd@K$wnt(J3A$vvf>qRNhK8K(d@1_%E0pajbL^E>b+w10 z$12YxR?^BlrIYCGFZFk`9+9ro=o3ommcP?;oXz=ctmQeAI8+b|10fY*e;-hjSK=}PYMD; z@?S0;D_Ncc6^OC!{ZH_QrFxabNJKiC2P;9ieZ(FfR}b3)UB)PM03M2eDXwI&I``$e zhSHeY0}XA<`H+HLz-g1p{b-5G0UwP#b0Xu}W%*e?dDmfiEpn?&0oZ#N{(3JiBlHF=lA{gD;?$182@qgLqX z`@8=ml_Yh^^Z1$p_lHUvCfa za(CqMVTb&<(_<)jm#EP_^u`+2?Th4w@Jb}GOi0M>L$S%@#qLw_Z{{Gb7&=q5*RoZr zH7zuaz#g}Oft;r+-dZO;Q+`W#NwAEP)hCcI<1ehMn|gc3?Kwy7Vw#FvH1%^Dtejk_ z8?d@`oo8fLcsoa$N}ge^r4Yxch0m8!T{TN!Em866NDOwcr*k80IE=ZYMKhm3(QIj~ z_!E@8&=Udq>cTBQZG~W0H_G_D^8&KqsU2Ttcz52d>(&0)$5+N-)?kg!S-u(JW6r_n2FOA_+foNGZ&V-yy$u;otNY{Ap4gUoOG`WG z>=`a16|)#q`X@YdTk8yOhgnaY+9c{=$hE9%OA<2jki-#J4EKYm1x@~h@w3FC9f2qk z%*$RoT*{%&_C*v%#ub_j|X)SfmB>7BPof z*9SD3&-S!?NS*c!DG{>a=Be@~eL}8-4BXWWwB0?L##@TuqBqN2V>px=+vQH-T!z3Y zrE+AT$ZY^e!Ny~337gK8^pk8x16bI%DF)6}gSqTqPm7q)=two@ z4-*ic#i#Q@(P!mdrPq_$gYDl`B1=;xb^-=4Xm0+m1w2Un)7$Zku73J*WI&Fp4r5(^ zl2CSt6TFKfNwiFKRbQJcMPXsKo%+eyrH;G>%As1aEU<#4Ic{9UzlYI??}PU^q0MgG zn^no9N!`?p6MmV;Jm5o)^p}6a#_lgN+DJluj>S#j#-z>d5*AvWosS5T z#2~*!H%TvIuafF925qGiw18Mfw=x!EFMbXxb0>!_E-WuK)r`cPYHEvSpx>uFnQ*Uf zm}gTj?Z^D4)MqR=URl5iXVHN?@zX~N@qUrnMSpi&TRJtz#pmqlRXIgs%H&#ByL97m zg324wDzCEnMB1$GCrI81s|XSsL*Akxu)N{cr?n1;%mkMX57@f0Qs6!>i>G(H{%*UO z7>188(rO;%6KOj1Obka&H&M04K^CNZY4mfCtd^B-X@0q(@`dTMb1G3;s4P^j#)Y+y zWT{jKK70CeXri0|ZDxw9b&j$oa)lo;u{t;HQ@45m*IU*0c(0e-wtk9D2QF+?Kq~Ts z@x1{cYv`@oFH|sI9mspuF2yRdg|q%d6Ms?oMX;SX)P?;=GkF&)GTVnYiTwt~0JqJ8 zf?U@H&L~aLE;|V{i(DX?#_#;$x4oof0v!2g38x)(8QIp8liDmG)Abn{F?Y0W3fxZ} zhgkgshuD(WU5%=#WtqfSa%>M5npS9l<#?6n?xum>URGcG1c?6ZovQR0LYtqoYAsm2 zbS7>)NJ^fm(bMpWqIxk)yTp&Wvl0lw&(G;lWaIUEyIb2dCKhb2eZoD=PEH#iwVw)q zcfukG<%Yj>9OX#Cq2*6bUbc#*;vIvSIuFJoG$RH_txja=Mr0d>ZN!ct!CLBD=8~jv zz(hGr{^XX*1YDriiy6AEm07144Za}Q_$yl7#9)NK+qcHL>Y14-zU|!b%pfjt%b&B* z%1$s6Gl8zhPZ|P~5rspZNW+GnDZEZDm;f3hP$t#%!;Ta7MPt#$6WLVbBzYp`EqWl6 zTK=9h75{L=o#zzYF?DzFR`nM`sPge^1e7)^jS(QN3LN4H=pQ0h{^;SJh5iuZX=SQv zrWK;Z^3i3k)3eGHdxCS)Fn-4(id4x%#rM}PH7qjJeAh`88rp#as-K*_o6-Vvgo2p+ zy~_slsM^U%p{8%NKfwho9mdNb!$-EHcJW&mD`vJFnjr__jRvy`Rn+y`8=xb%qm;_n zU$*vrAL)13>3Q7f>K zk-?e@@ja*mYWZ@r$fd55;ZfOB6qKrs-~#S$_60zPZ7++pgn(bEX@@A4=uupmL4<8T z%OQcLL?~v>me}Z?{t#$?TBY({Rxjs`l^qBefAPM1m;!^&|JPPW`=g5eM{AbQ{7di; z5OJS>_ln)~M((jjf}to>yAPbMrc2FQ4|>_0J3^H-@BL|QOZA|SfQpNX0#q8CmpkEK zZtcEs&K#%jEbK9sdI=9+7_)>mpn~o5xG*tWTzcgKxLmh~VGsK4papBXhE-)5aHpa2 z46@5oR{A9`cPDdxG=b{W*;FEEZ7GBel~!rKj^!F}uidO(?a8=8#5Y zGg=@2Ms5ksH4ImNVPJ^2)-ZeO+iJi3bc6Qp9=ihFW)F^5+ebrXxPLQ7)M|vkvEva! zZC2T;VPO>0ai<=5=5HI8H8ma}N8qre)AqGf+umsYnH_d+@%TVUyzaTP4>EJgvZfDH zFL6$78H|76oLE}x`9#B{#o(JZ?7ykgmP!0+_cR+b0C}#92 z5Z?RtuKXnI`=jHJv#S!E4Sk{W33KnCQc`+&J?FYXlUT-N8&q$q$?Q3^G`4{t@o5yO zfNf1(Hv6qmfAiq;`cCVIxr+^0Qa{*QJVpL0x1N?z#UK8uR{teBKL7Q;6zj+_$I?G2 z@=hY}3pbM=Zak+ZeEg_5b@R2;n z8&Q_sOu%m@VzPtSkr~Js@Oz%|T>q^&!VwA=YU!#Qd`DEK8U4|7`pSwv?&X)mYz$vm z!`)mP|87>te=;oY%Ai&kLU{_ugEHwGv$i3~C@kZ2+~8A~weJHqIA-bQl`}X=|D$ge z)!7o`E3>8s@ifjV+gWZoLnF*6?QPWQVxzkEoK@aJxUn4$^U&|;RzQ8ZiEo88D4%LT z>l%Lvb22i@x)lhx7;7J%V*d{W7jZEDVWgklHdg{JU#V+J{~ZuLbiwIDj-F2F4K^sr zFIBYsL36hhJ7EWEm=J`#B3fM=>xS*u2y5SpH(7~wor_oD1dmaHd{eai&}-&PcJxUr zfoz$clM|OS8GTcR&S8em(ihY%f1M*jEO;8>Hrhu`IaOy0N(dp<3PoZ(pg$)UytCIr z*P$-#M7llWe$r@nBJ%eL3n9`0ks9(L37PJvE-eYLfpkE}kM{E<(+`bjL?@Ts#QUpf z*8ysz*dI>(Eb4p83k&=6eL9S9vR>P)-JLCz%iOZQr1_-zucWh5ze5-v_B^tWKcwAT>FA3jl^!veo zIK=VkPxi%avK^oh(J@Sf_zAf+rbgxRxHR z?jSND`;sCZvv3_UIN=rHw(LVRx8Z!-@M^iiiF2ZIRP1KP0R*4kLyO~>Mn1T_xpUNo zHfF#TTp!3Tfv`=F)V^!8U3hN+gTUzS%onhxzK$KZj!zCB#4O|?`#LebUBA@x5uVLI zb`JhnzK3KSbiL0u1^u9q3eh(Y0Z~)<+5DI&{^v`vcZ0a$sH2uxSXQ0wa&E&|2x)60xAB*J_C8bd9~@KdVs5w<=$9Y5a(RywmM9=pfS+R zs(XP`RWqyV{UJ`ZV$6rZ3yv1Bw9k4vxq#;_Rr|n4&vVaafFAR7rn&>rIkXCXpKRo0 zPmF@8@4CBpx<5X2-@suyyl<~^YSgj2@MeDc(9;|=&vA(uV^}SL(E)j$c!}8jMn=f6 zlQKP;%*Xd6Y8X@ZHvU2>ie+-tc`sDv`lk~7pXJ1M5~Lgo(Vj$^r04#?BJ%$gRPPMP zM~?hgu}g*h|H{S+poBoZElm`Gp(e;Gv;jL72q!R%>`_HES^NTa*suniB%?ImlIR8| zN%6D_di3)&M(Z#9&&~8cPNwjBKZU(7rq31NqDP%s2-o8(@_zt`&vkC5*Yx{i-jNBk zCy3bm-C0YL$%3MX$yig8?$m61xF_bn#G$cFI5Z3HwD!U9w-1niKPloxI~66JnW7lg z7H!Sk&PZ(t`bFA_EB?JfEvxX)G8lj1eK?5o zm^=$;EX%ABUFQsE^V*7iF2zqqruqxw>e7233C7JY209V~wpurK`%0QziG1D5AHn7# zPle^V%@!R^4sxJFb=qu=4CpSsA>28#{pjloI7d~rBrje&!>qJY7usyhoHcK@+kJ&* z7Fo**GZslV>DerTap!-1T*4vMztkB-^Vt!z2{+lgvS!ERIYy;%oRn~8;_B+hWYzyV zg)8%sfzDDk5~3o{zD&>Ta?nKNQbJ*EE$S@Ab@4XYF)0DJ7Xw(lYf~M{!^=9){tQ!W zwR0>_$nKYJEXJtI&*D!0vfwfE5ykYbGGn~8Qgm0JzNCiM)9}f~EkVJ8Sg~$NII@_N zZQ?moFVH+7+YSjPsiX0NZijHwPv8ZWt&|)@T0--{wAx6%7Y~Yy0SiGE$v}I>^n;TV z!E&SaImZJ^Y*$GSk5;BCJq7@o>tInqpoSZqQpQI&JMq}eAQuLrQigI&m_#k z*cZaqTkYcx;UNtV=^*FtIUZbw&gR`&#L1=uY;VXYwi^{_=^{ z!>j`fmb>nMphZ__%cG2fbQ>wR>zpmBFXi7gVvnXIcx^VboQ(tv`TVNcV|@3fhcLts zY!U7|XHn9>o`G@b$1*%u>`BfmmoqH2d2Dlz*z%q!zlvp+XZL zU4nS21)0n)71Ohl^282{#Ped0Th!V}L;nrVjkg3(2t5uN!#zb=Je@U7|dUuVrA4>))@^pYI_YLCd3^OQh2 zuYz$1glF)CmviRje|b^fb@}5x%GwCQz`7#=-AIC69oe)T@%!LCkHhT`vqoYI1S@ZD)B92m{L3WX>ihEo#LNR%W2B zs2b6Bq0_a2ZI;WO%X%)YXQjbE-pwJ6dBlz#ObDt{Ai(>R^b{C5RCnv;~@()eRAR5VaGS9Jz}SxQ*+(NSIQ7~ zX&(ZtJzT+WlYawn1yKf}&3_UQB8EGtgOQ`>;$iN3Roo7LYjZFdvOT&RLcB$fB8Rfj zu_I4R=03hGG84rd`T+@I_DM5|v@?-YZn*7;gq>48HX8CC!Y^(&o z0>|%l5+6I61&Jk?`bcE^a7NSb%~@{fRQxavqYtm%B_a0I8+yu_ICz)VTg_V>t(?Zw z5`PGX$;F0y$%{?lOuoBYc62Q@rxo1F?4~CaYP1>}wq`Kx7=V_Jt4$KW2BY9We`Txo z3c|{@zaid9$0Ef(Yv!vrAv)sq@@LduQe?Hy^f>$pH&AXEX|1=y|1)>6Bxqm5q-M|A z#NM=9%08p06OHZ5PA3%+9_7(zl`hUx=P-5lqA!W>33FCHyRB#{I+zu(oOPbl&?2vS zw{2=o(Zt){=?ZKYQ)^-0Vs|sy!2M6MZciJg*hxkxD;i5CGQe_{6Ii!Np-qOkhnbJIP+* zX^AMd^R>r3%QV1QEXt-I{Su5!LjZ(6FpTXjwZ@PyZJU^9#$jF&<;pH+)mpY$Bj^)? zW-r<4waY5uCG0t*{RFiQDtgutT>0PBbmO*i@H6_x^O5QStQk$Jw)3 zp?2GfhXxMrD@tI|TG{&Z(%`QVPDJi#)M^pxy@@1!mLQuo2U-3qvtAZt5Gsh2%Ay2B zhLFx?k&q#R@>+!+M5!dxIW4kqvs~@A%X>+Xi22 z^a?gRDx=fbg1;+js!HxvVm7-fw0gAjak0E9OaqCS;UJe%8Dh$E#66?i8mp{$#ruLa z{`L1%tmmi0=~kcbm@nyhq-<41vG<9sibT>|?xm6zANsxvMps|oOS4$wtNB#oVYc{k zj!q99@mb6FoYaWQ@teA2vfXs_rGM-WiZe~);iacxjUlW5MG-;^6SAnxHJhs5h^tnQ zF#$$L)ozp>{=rc*F0L3FV8j#}RH~#uqHAKTEvSCSCDM9u*{(P|E2;}N(Qc;J_$vws zpozF_l@yf7a8G~Ha=E{X0#85Ua=F*zv4T0z2(8$nw_rm;SkgK41w(0`p@D8h8a(i% zm3KaTBYQ`FFOCwLT+eTe?j=`Slx{wp4FYzG-{X}<^&~LXDz4NOCC*A0hDB?7gu0g= zGQHDv`&(k7IS2q`e2({atMnkpiz$D0ZfnM#cddW#D?=BmJaF*~LQ9&LYur!3K83Zn z&ayI%u$5vFt-GpARAJ4Zr@eZ~wKlH$=9MxyGo)AY)97t2p%Uk=!29^cC(e$IzW_T* zl`^|48;r6#ss{RLV&(rufFiSLWQjel6iw{ROf*&?c5| z&&VVPfmXIEkd44t&&er?JeD}f)SD|ijc}e7^&D73zGbv)nl3b}4e|t3?j&cWF4faq zcD5K8YmQz8IBPueGGSaa`cbw+_4VfpA~Thab+MYP{jM$R!Z^1HH*& z4A*rQY{1)6ZNvwvPxxL}?E)mWye~fj`x&Skc0n*os3xtmNjUb|Xg3&pN$q>N(2sv# zo7FG(yVCeQX+C|IvE4p5L${vkjqYk7ojX71I{Cm~2gQ=FKM$%ocfXAoqkG|2PKKz4wcg(`U4bL;65tr~!*fH^YbFQ=c4RCj)vK{h_}cGg({+ z(PrjkO6)?uM8FcH_dyT3ykv$D-AC(){HN+W39?&M<$@rg1J zUo0mWJQV*z9YM2~T1b#45;4V(?IQfz+Po7}J2W7gH=1NmuDhvY`YPC6x{Xh<`}E=InBdGgT@K|JnHL1P@8-T2+zFgbVu zM&`tpCOWP-MTV6zP!h7SgX9tQL>Z}i#BEq z1rg!Gl2a@`0$|M-B~NDN4DzsXzcJVH*7IsWRl5AnSM82j{jzHV9$~F7ii%HjxD@od!DhQdL9Jfd zs7#-kHCAS?&(Wq-d==ofZnQ;8kK?p`@rNp}HBzmm9tta})Qvm)1p=xj9Q4wB6^Z#A zSJ$C&Dyn2-_R|+t&G4_nLO-aB!SFI(UTE$<{53Q`f(_6~08X(UP>h<|-VretU_yGJ z#}mEF0@mYQ5c_H{)<2C*$_sE!;?WZ;zFZU^q8e+h#0bTNKv&lor?0Z~GmUXVKeMPp z2R}kup_6OvUYZoL#*Jn7L@u4lY)uFH`FYzP^UtA8{npd+Uq!6khxZ(D)fjB`3L4+L zcvy!6asZ!N1y488p5P;^qGi$~<=7%yyEp5#v1QvBEH{O&@~W;@=u^4F( zj-aSlI23`-kg#KDM%Gp=SWe?cj{uwGh%KHrVxS^3wC~i=j??bI{q_Xk5mj5U-a?=j zb4h#JV{DNuoYD)%)VjjoB=*QgSVZ2nJ=I5xjc&}W+_DYBD~{2|1T}`;TIG{NdqM1M zIlOFvSh!lWPc1>n|BXCD@k*+j{zIOkI3OUj|Nl^#rHiqdojFr75DF&Qhzt+tg)xr# z1Fg8ZY@@*qTQZyi4!@Ex7Z}?(fTDv&l7LcR5oF(!gLQF#p}Bpda+qdPz7e@B-I&ZR zpm>Op!%h7sO5qxVTG7+zZuX*=@59p5op*;p)UXeE_NK@CyVv^;QAzOWz8GXJI+nKW zfDse_p0%?_pRr!~UKSG)O+OH*MZp=zAEm-P0k zkK#I*h1)GES_19Y7)9z)c=YSm-z81=tm;6%Q@wY9yn8qbfc2@5I`PU6eyi3FL$1&c zZ-ygMRw3`)jzFe;PasCFsu+#XC}AP(r#g8|&?kB|or;gIxIaSe1gZlEc`%7bO=*e7 z$JPA^@|7QC_zU;d-~?+;*z(jLaQ)We=Vh|yzaAhV4O8+BlOmKRhneIgl|CLYC(o+` z{1gV)OBp;TPw)e_8G-#5WYy2 zQ7SgQ3tD@tkDP#x<3YgTZl5kbq_^vDCfLo`3u7=0)l9_t!Rty6Iek>oNRe5 z&m9~ESBT6@*#;odwYKt(}NhZA!o^HxA9`7YVE<`uP&h>{OnJe zOHdLZHc-hI%ZVd#Rb|+U#-X0Y)o*_l%;UvUL9TgZMkWI9rA3M3a1WAU{r$G3 zLp?XQL@d{9rHhHMes5s+$d471lt5(*2#%o`H1CAy zEl*BkX=5U%jTQlJvEzCJPCGEo|MH7(x`a2M|2D{*F_y#Tc`dE6qUQ%s_u%dMth{D) zjq$)5n@L2iyp}LfId4Li(K;37Rm4q9F-Pkkg9kcsrn&i!SN>+PR}2&6&$$8*N_B58 zX7Qy4ujVx&h}AkSSU($cNvu|Ai+SwgXBRP;CN$b^82Sc6^ZF`{GkmK{8Xvx!%IkgP z%HU#abS+`B8Brv0N}jaD#rj`042JG}pf8EvEwkr2xt>=3bT z)$f@Q|BV45(tJAV)z(h5W3cCr8g}?`<1;^*Z3A6_E~>*%&e_k0Y|T5I3XMwbl6uK9 zrZJctqz^ySVT{7H(2p85)-uufxcLTh?Xj4m^hQI$jGiq*6Q)3HsF$301>Am8{B0bCb@2HckN|uG)nL5ziPF zRMEtYJo&ig=E7nNvg0azlQS4Xg$&a|nCxv-0qankbqd>T%Tnw4L{rI;r4hJBRapdU zlT-|Bge|U4ngjh49YI|NMhmOAEK+>Rjy6W+5tS8P_-=Lh?)=<+J? z&1L?<2nkDI+1)L!Y2}EOiD)&sHZpM~1GSy8L^r%X2R2SpN)^_ihQr&OYY*qoBiwbR zY2TVx9Hfq!$D%169!t%2owmSE<0CZWXs#(lU6xew#Np6XN2#DCQ$$6|Ds8nv0V8($ zSY6*eK*Zb|8`_nxTdmf^R}rP{qILurk38+Y+?{=*MBjWfd*SoZcuypOeS$<2`iqO& zv|f{C2Z>+FQX@bw&}2h%fi$A0X-qA2IhKFErz?!%?C`0ZZyFMjh~BmPj*&`P9!Uao zW(Hm<{KMPavL$cd-i@^o(45Y&((roeVzz=d<|UtAEK4>SGVA^XSG>-w#U}(*%`)T! zw@uE8PvJH+|1cB<>+m@6BWk& z(lJLg0+K+6IVw-QtF+G$aU)V7&NA*e9e=R%M93IjLbQO@4WAix0>@?OmGEwb!JUL> z-owsWjLWKf-kGZIgS?2~Y{dPC_q=A`PzW(OYGh`UIr~g*aWi=0*3+X6U&wGC*Umrw zov{qN7xiz^9yUUko4?E-B*nwq|Ca9YoIK}zc04jq5i6a?3U7_RK{Snl;BBDyq`&{` zT@>%j>V^ay1^{g9U4YEChC|SW5#~p|m?DE3=M#TiDx>n#?`#yD9 zAs~cO?|Pvy;gJ$1&f$kqu)cn1@LhP+jSbn+j&~Sv;Z({Shwz_E%1|MF!BYFgx;TJK z!bixJUG}Ku1&aYnG<{m&V2B!k+}vAEq(OLz5$+Rj38n=@v8YG_mNpbiu`me`y{w%i zsgXdv%J5UrZ;d9>sk5ot!l=z28Lhigu*n=|E;d+1Lw|;+%@Uc44x?bas=Oc+w2I#0 z1;=sRN)_A6mUbr@c!kI7!(d-qI((@T21PGL&N#|@Ez0(~Y_A1073gh7g(@s>fNeKn zbC7~J2(IN0p?kp5wZDtyBO=>X;q!}s~mK)5YEn7=4_3ZUfO6q?+>`h8agc;gVhH*$Fb^y=`C&eAXiZ`JC)v{ z+TrVNEbv9%pnI+HXl1U!>btL{X(;aL-`X?znB6@+I8>8=#Rko> zdwAAok8J?SS=Me&RJ4}=V0lM$1^2o7UN`sloC};y_*1qm3@%ODzcMxCPVUX#?Y!2T z&O?2o|D`QDrKAVdkS|G5>j<5Ree11PMWT1AqNK=JUO^-d*dinY-@qx6eN3?z2zceG@8-rW-iz zUh){9acbv<17pW!1?)fPuo&g^ zOjnl+U1H)=ep-=5iDpP1D11u$TFXcskf(5+gsx?5NIJOOmBxNH!hSZ%bP{caLa&hd zm_O2fc>VdVGj^*r&X>Gv1lV5hdby(+<~;!51RX-j&IH!@tck0McP+wA#}!86#DW*6 zu@Gp49I}9F`SeMilp5Bvk}?VIzzJi9G=(R2dnPtEge?wpK6NQ#kAh(S(@ z%G1ZW^z}B)%#~Emdc+!C$;4Z(HBX4Y^wj1qmutg=Ltn7Slgi8|Tb=0q$c@fgSX( z1FE_tu7$y_x8{ZVMOkn&3l->xD?_xcGornd?r9)7baeo{bAfT8MoKvygxN`B^i82T zn{UDZnAmQGvXE^!X^cWn!F(EjqbJUi4_=<^saz_>eyzHEm15{j5Z)JmYr)-Ppn5p{ z;s~|`e_Y~g0m%Vd9&D8$^)%}z16NZ^l7$3ez1)hrM%uqHl_jBYig&lLKTOR4cglCf9I7W~YI{VYJjK`*$|-nnvrprXr3mn8ynlNuHsB$%Y9T~s2g;yBDK_=088>TgU&W`9uJ3s2 zllr*W(z8{c7^GN`(P+NLJicnQK2Rr$pQ5JFdC4SUZ47=Cu*nkOL+auv?whopL@=*G zB~fAbh}Cz-*W{%_I_(>ceEP)~GeaGDklhb47^4CHHLg@vyG**vC3AkUMD!%;5En4Z z!|^tvq+=gVGg8$i=l(|UbP<(}pv=+N?Kjf|xKhLJ!jSORub{D0+xe2lG9I~b`{&OS z2uh?^@jkhtH-Yw}gkD%wUOaqYw9EdzkD;Ih{sL3_l@xJ8LUII&-8-hr!c5A>Nx`}B zYhX)<`_^6?Rfa@W-PbQqG(9ux&r4Lmq7eCh(`T}7MGpo8%*+DGhQFip@De6Yoi_T0 zr(B=mj?BjvSa=N`_X&2d3w1V`G*4rjXC=2=zd3m|EB5X3LIq%1Iu>!lYbNE%wbL^4 z^jfO)!5)y;MjA!VsucZ0xCG*CSPmT#Nf3(`b+`nofy8J0n0O;v75@3xtt-lEVCclz z5g@tK5X0p%hQNdM^8>KGNm>isS?9+wM%nHc2rGWGoMYM-lZ-40!MR-5Bt8PzvvGZ0 zDy4<;-H$^+Jy^poh@lM6M7OklaCFh?zxlMF#}~3u^TSa1^E;Ndn0%~;N<%i#G82_D~KcAPRS%NXX|FIr4H(Tl@1>hSM6m(_Hz za*|EdVkhsBG0s!r=ixS%Tu(lJM_{?SN5$-P@?KL&UNcLuM8MKIKQH*y2{W%qc1SXc z!I2~HTl+6mD^^uUb_2?I9J?p47mRTE#Qj5D6XiT&<=oQ+2*>^3s|}~94XuKYsO*hd zeu9Ds9g?UNoagM&QIU@lW=zgJE7(9JF7gFzFEvqczyL-+|#^n~fX_ zdH6dKbYe{9NQsdF0D0)ne`@Hie+uyb;^PEp27~b|h+mo4wD1U_3liXHF-GMBZTD$V zV```AqTCjFJ6TcmpE*?v&9g2K+gg~TsM)m@f48rrP|r2M*Nu#?w`Ck(S636(Dbvus zBD^A;-cDKmimfrsaS?HLxw7GvvF)|}J`S7#M(S|8QU#d?R3ZpYEOC&4bwL}9j_RMB z6#BAJk^0qBI}n&f{V_wgXidiam?^rXLEZ{|{p!3MxXj6iviO)AswNjxs1z#DNem6% z4AmnKZ?Rs&Bl-zplI*>HJa6q@&BMm93g+|Vkdbpwz{;3p%NvJ6clcGcBeqnvN>zn+ zwy;#37**7CwL@~GRrN!#2-2$NAp(-};CWWa)OmDR0Z5p3qaTM#EHo+KIMUp;pNkBI z{0KL~hV^ShS*mdharfHJdGK1RkVB$9Ls3gSTb9%H^e1xF%k?ALoR><#Oy{Ab_VvV!Wqj_k7S z9VeqDU9y<*9uEzHVXZCyb|V?&NXnVW&j{Mf3Yb0mLZHE>1Rm!;M~a zVUiUa8#*s$<(kdu&ZW!nmifai6ijK}aoWt?KbbEjjEbAC;b~68K8_hb7_~_3SNu+P zP)9IQnpZ${29$jdPJN$yTwlfAxq?EH-BlCA%p^01!*>TmyNrTCV9;CRW%%b!M*7llx0#%1 zxz|G{D!qZk-3ln>70gO<@@#Q=sEe!xGUHL3YG^@wpM=AE%UaWTC5t_zw%S?Ng#4;6p*|Mw4ndmgi&Vy>-wc;qqkBYF>q`H ziI~G$AoNWjed-ivI0`X`l|Urgr)_5?Y2hc~YJ3lSa+AepD3%2b>8O2we->Dx7m{DpIQyMT z;8Mh9jpVovTV^FfW}JEFABvbRaXBPa`6S|tJcb3d=&yN>h^n1~F&DiZTjiw|xc;pQr5$?-_ zZ2MWtg5rU(a=HGmZK~?km^7^QEJvpbyQIuU)Zgv zOyuHM?fdCL@C}uHarfi%Ytjz`i0bOnC|`=X)pQ@uPGi>)EEw?uDrcegEL;sZ*KoWivrSB{2=6spm$?d^1QEL8}*Ly-nW z+U#BmC86t5i)r#S*&3#AxHEl;PSQ6Pnle+U*&Y&Dr`3+q4a$!c0c1 z30)|RU#8KLQ5b3y?Jn~y4V-PbwJ)y(10EXj-xH)*Zy68L6?|7_-G1Nr$Y)J}Rg}x% z@$PrH{rONI+kSx;O|xUKe9mxk_tR?*UC+{=N`POkYM(QqrXH0pce)|GvE_FMjA{+BCyq8j@L=FZ8E#ycu4l?y=5za zo|=u?1;e`6i}od4nzyq<-fZEv3d?Ssuw3JP+$AF*1q8@Mt23}ws% zhf@qjvs4O^&3|tKI%9 z;0Q#IEC}vdG!UcdoNE3YS@nX^SU}cX_^G|H1aAgCfaBSQFsS0WTR`MAT|>})bpf#aYcLjut~wFQ$W1Zy+sP;^KgPJ?)>1e912Z*;oGCMppT zg+cC)LTOg|Dt%9Jm}am&8PWS=tQM;bIJ0s#N#@$UF-5KY!BV#y&C+|famsyMeX1pn zW+V3}F3#ui=9?f}W!`5c)ai$3J-EIyRO!f4Z;t%QqBl_T_|q*hPOGTx*0&EMwug!i z4#`HxkJ=Q(;Ms&%JSkk!Fq`7~ziv=VhUAC%#$k|s+$B@(R2FfaTIsblhTm!nm0~J) z2&8>n7WR?x8oriKvQR2mODKr@1 zOc)|q1-b;I3}FB<^QexFC^L=7s>{eKz8`+iIygMG@f<^0QRR?%M1`~R8E0iJH&-P$ zpSq(P5JQENo0E5w8KMFrCEbL-vyBo3Di*%4$6^F3mIMy+tD6GMr`%$XK#leFOp_)D z=?yiKVzUf%YrhpOMGDUcxCbAVF0N8dy;_pe=99kLByU)aq$ji0VV$!aC2@aW`(}dV z96$oJw#t3gFM=d30blRc1eWEsXI<}NTR8uH$Z)-F|X zq&G>+4mbCg%F^VRp6eVX|I0U_i%W!c>DE=LR)MRGnWs}TSLs7PTKxJAxY>y*Vjn4} z73KltO9)ZBS8y@|E-fnEtgn%z99!HS=j^2o^dGP!dVXv%w)pj*tU|m&^uI&S-Q>Kc z1mp{U=NeMrx_;-fhC1y0PXL4)cx$O5cQiCikskz@tPD1`%lejMRQEzNr76?FL` zHyEV~8y7uzC4Vm;UE6JNKmNR;V=(2u*AaX$LthOz_GrYdOx(s=z-1-ntli^a(P8DJ ze~e=lfOj<9fwcuBvYx2QG@W=INMD!uC|*6;R;52ALOsxz?el<93Uz$OW25mQ`(+cN zBAI^A8DVg_&I+kY9TT^XlTDW>R=V0HVFHiC2qkEWG4Ido69C6%cr?@gIh)F}d` zZd+RU<>(9jM;n1NAt645nGgeN>??^`H01u3pDW-}J)2rhk!owPBB5-OqqFy#UXp@w z)OoW+Sn*#jFM4B|{g+fCE3)TX zp0SrDm!tAEgIBI9H}zZdFpL1L>9#~T2)3htDw+|YnDW6Y-pD~@h%%Sa-yW%S>b7cr zUdje0=yvC*BceEchmuUR=uFaSd>&{?V*VqolUqdGPimb&L>s7wBHv=XkYr}1gJsx= zc&&iZk6ebpJfJEQoneXKNV@Ut#nW$+L651WoQw(_eG^aNnoqHHY`u;SmYd^6{J65x zXh(JDUu>pRm)Et6d%b7J=D_mIoU!uG_$AQ+mgXAnkJ{Ww+{7I?mc_fk)9_%RtNmb4 zg8h7jE?+!Gs8NFAfpf{gNA`*LG(ldUtcu8K(!<}r%T)-ob zQGs?0qf*dFOl*nT~W;FO~-?yT6}TJ@rB7~EYmzV;(R{vF5T=02GXLDdMPIG9?~i{j4+T)|0V@xfW{JA!Bm>HIgQM z@!^X~xitx|F_CS-v8J=uT5$QRpZA`1M1=8fnVo#o#Dq@p4PaF^6u)K%=B9JL@VQrIM!AdbgWo;*G9?^g`J$9OF_*Y-j&EdX zmdx)!B5%isPcLwIrOIMXW6++=CSq$I!|!=L>oQYP@|RtIt>QT){W772tfCUWsb?sQ zLr89&W63MjIT5q>;QrU0dvOEgtV4 z!5ofQ{9+0VsolpbRh$)OM(`TB>*k0!mu+t#t5vx{z234ja7dCl3ns& zvy1tOS&gY@f1Lno4oipk)=i*pm#acJOnuuO+IWS1PF(S*594!|0pL9~9jo%FRMDVA zKSVgcll^r$w*cyxIKXRTVZ@9l%qlxqX#+8}<-@7ePH;zPLek$JG_zChjk*B3-NXW%Fo`%>N*-XY6q|$VP9QhMc`VmET=~yt`B+R>D$7F*pILYqO za?PO&<)C=0jPjUXwuM1_IuBz(v?dwm>oOxN1uVhiJlPn3^mC$s9-CTJ<54Y_(Q3HN zr-^i3Wb?QKm*-Z)=U-SU#vh4!D5x@3$IcVh(m-g!fJH?x5>P%3w#;8 z2zTqkgu^h>-D|qIW0<-^Q8#-3?)|HGLvX)G^f&UjZ8huVpmhfk8kkfH!WBmciScCy zi{Xjzc(Z;qiLO7_?hT3OXvzCwLKQA&B2H&5K6M6nFV%eB&Mg5N(gysoHzE1l$oo{9 z_<)sE0qq{HM55RBXw&wn$jj613WrrV-a}yo1Z8fS&-A_`ZLvT#mqzPM`;SGwK7nTP zg3JOYZ{t`VpdZx>#iEnyZw|bik~Q?~2WLB|(yhAQ?-IpMpS)xw!W3oBSQFDduiiMH zJW|gb1#uk4^4PaPc-DeDzC*Y3Z^%^Gp{*vw9yWjRG`0B}oW)k=TgKX@KHU43i2{gBNiA|96I|VU>0GT>t6YXA8T>t! zb;Y!S%ipcJY^5T%=JvwKXCcj^-Qd%Z`?>d%4Kj&CzU;{2_=)llWn*$;l)Gl9$qI$C z0x5%cH4iyHIht81?ouhQKf2f=dky@Qm4PBlL?4Mv{vf4dh677z;UcVORN?Yy{tZ(F zwr~KQGHo{xXA^O?#&!~R$!Wa`GRvkDL|Ex1rxRKO_WQQ%-gwVO`A#`hpAz8sv#3^mRKDb-Z5~=nC9PTW_-X7|LYP@!^4G^;T@Wa#Pbr zgHMZstytV%qa@*(JDR_hy!JaN-}Ey4f@Y^DubJ_a{1=96hA)*_Wu7)PkkskC#eoC( zGS%mNqrwn;-x2Atj6l#LO+0;SFZoKTOk=1reJRaILB`R92nunjtX6*?)c5dhk1yY_4g`8{GGge9|*O2P_0w1Ud!~%(m134%FVYq#qr}On>WFV56>RrjG1t8 zZPOSm+47+`WRRsY#f$wGZ({Aef`fXRZqB7QMr6)pz#V6z9Zoo;BOKRYtI9N&lCPq! zsH<8A%6YDGLc`x;yK|pTZ3GeAttBg>@o1@VAvudDEQmJAXY)yL0)89b*g}4Of{bfd zo~Lvjepx92alU7%Hj=81HW@3?k8+LTBt$_xQIvXwg?tko|4`huibtk_^Rr(d^%KtD znfDmDCkfCLQHktZ#swHc$y;y~xvlFpA}W|>BtP|}a%=K2qOdB;dS+9A?U^={o1a%C z7lQ<&;M?X-EMN4v1QqepOI86X!sgKXcCo`r$^F#qERrZwAv>dPl&RgjM{7+V%935W zUF72%-^F=~+0oz~0YUsFaW1`X)t4Kj$MXe?15BOer@(>vrs!2oG&h=c0+pfN1dkVa zLOFYz>tOvZ15FN(Vn;IYX^qqfYvD`2^e#&l27b`*AL#7f9Z<)+Fx&DxY*tMi?^t9ykuQXb)6b(zZ5_` zvaV_0sN2PvSy#*$*H#WBpW*u$|9p`C*N2(rJ>hQ59pjla*)!F-_TB7!!kHg^>=KoP z_*TE@rY$eSE4^N3^n%UDi;pA}Pu&?myNo>$B-*DZG~c=I6+DbY?sE{SI~ZqG5?NL} z-^%d&DUN{2Q1o4>iz>h4pe*P0aU%d3o&%`#cxul1`T(q99pdN{k z!#%8OY5!2^O3Ali0<1iTu<-y#`~*vOBlI&yq=d9{=e=|16Nh7jpzmbbEpkWtHru}P z1hUR)eekmOA8CW$rQ|Sui_65`Hi~0)PMP%PJ;m7oB4&Iawu5LF?P-fwZ)$h3i7$Wd zpZa#fCF=BJ`Z25vgQT_Mw?f-XDrkFI1K)P|XLkI?JJokozDR`LD~7JKia^~PGo)jj zpvEyW6~?7G*=xWw(BwXk5bwIc@y=Z z@5G%zyU5N~8al-Mx;Rw}yxMkyntTZ9qKJ;*ysJl6lJ{%_oQ0DuQZ z^nK)8A}TS}U!;E{7y|%oFeGw}ThhP~(KP7h~^qcC(nidx)i z!0As0^UQA~{xewuZKU{N)G%_mRYNfb3&zphs}YJvdmw|o6x8BEK!Ae7_{wSy~YsR_c6daEJh(@$V*z~|Ng6Kij!VWwgKt>3Sao1#3ZhayD& nO6#Bg5Huaw<|d#SQq@gO@Jt>FS|M)S0~Y`b8vsCQxw-m3w$VVy diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e18cba7..949819d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon Sep 14 12:28:28 PDT 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip diff --git a/gradlew b/gradlew index 91a7e26..cccdd3d 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,20 +6,38 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,31 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -90,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -114,6 +113,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` @@ -154,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat old mode 100755 new mode 100644 index aec9973..e95643d --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/src/main/java/kdp/blockdrops/BlockDrops.java b/src/main/java/kdp/blockdrops/BlockDrops.java new file mode 100644 index 0000000..9b45f29 --- /dev/null +++ b/src/main/java/kdp/blockdrops/BlockDrops.java @@ -0,0 +1,223 @@ +package kdp.blockdrops; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableList; +import com.mojang.authlib.GameProfile; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.enchantment.Enchantments; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.inventory.EquipmentSlotType; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.NonNullList; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.dimension.DimensionType; +import net.minecraft.world.server.ServerWorld; +import net.minecraft.world.storage.loot.LootContext; +import net.minecraft.world.storage.loot.LootParameters; +import net.minecraftforge.common.ForgeConfigSpec; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.util.FakePlayerFactory; +import net.minecraftforge.event.ForgeEventFactory; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.config.ModConfig; +import net.minecraftforge.fml.event.server.FMLServerStartingEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.fml.loading.moddiscovery.ModInfo; +import net.minecraftforge.registries.ForgeRegistries; + +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import it.unimi.dsi.fastutil.Hash; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; + +@Mod(BlockDrops.MOD_ID) +public class BlockDrops { + + public static final String MOD_ID = "blockdrops"; + public static final Logger LOG = LogManager.getLogger(BlockDrops.class); + public static final ResourceLocation RL = new ResourceLocation(MOD_ID, "drops"); + + private static ForgeConfigSpec.BooleanValue all, showChance, showMinMax, multithreaded; + private static ForgeConfigSpec.IntValue iterations; + private static ForgeConfigSpec.ConfigValue> blacklistedMods; + + public static UUID uuid = UUID.fromString("1ef41968-f9b8-4350-834e-367f49476a56"); + + private static Hash.Strategy strategy = new Hash.Strategy() { + @Override + public int hashCode(ItemStack o) { + return o == null || o.isEmpty() ? 0 : o.getItem().hashCode(); + } + + @Override + public boolean equals(ItemStack a, ItemStack b) { + return a != null && b != null && a.getItem() == b.getItem(); + } + }; + + public BlockDrops() { + Pair pairCommon = new ForgeConfigSpec.Builder().configure(b -> { + all = b.comment("Show block drops of any block").define("allBlocks", false); + multithreaded = b.comment("Multithreaded calculation of drops").define("multithreaded", true); + iterations = b + .comment("Amount of calculation iterations. The higher the more precise the calculation results") + .defineInRange("iterations", 4000, 1, 50000); + blacklistedMods = b.comment("Mod IDs of mods that won't be scanned").define("blacklistedMods", + Arrays.asList("flatcoloredblocks", "chisel", "xtones", "wallpapercraft", "sonarcore", + "microblockcbe")); + return null; + }); + ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, pairCommon.getValue()); + Pair pairClient = new ForgeConfigSpec.Builder().configure(b -> { + showChance = b.comment("Show chance of drops").define("showChance", true); + showMinMax = b.comment("Show minimum and maximum of drops").define("showMinMax", true); + return null; + }); + ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, pairClient.getValue()); + MinecraftForge.EVENT_BUS.register(this); + FMLJavaModLoadingContext.get().getModEventBus().register(this); + } + + public static List getAllRecipes(Set allowedIDs, FMLServerStartingEvent event) { + List result = new ArrayList<>(); + ServerWorld world = event.getServer().getWorld(DimensionType.OVERWORLD); + for (Block block : ForgeRegistries.BLOCKS) { + if (allowedIDs.contains(block.getRegistryName().getNamespace())) { + ImmutableList validStates = block.getStateContainer().getValidStates(); + Set dropStrings = new HashSet<>(); + for (BlockState state : validStates) { + List drops = getDrops(state, event); + String ds = drops.toString(); + if (drops.isEmpty() || dropStrings.contains(ds)) { + continue; + } + dropStrings.add(ds); + result.add(new DropRecipe(state.getBlock().getItem(world, BlockPos.ZERO, state), drops)); + } + } + } + return result; + } + + private static List getDrops(BlockState state, FMLServerStartingEvent event) { + try { + List result = new ArrayList<>(); + ServerWorld world = event.getServer().getWorld(DimensionType.OVERWORLD); + ServerPlayerEntity player = FakePlayerFactory.get(world, new GameProfile(uuid, "Hacker")); + ItemStack show = state.getBlock().getItem(world, BlockPos.ZERO, state); + if (show.isEmpty()) { + return result; + } + boolean eventCrashed = false; + int iteration = iterations.get(); + Int2ObjectOpenHashMap> resultMap = new Int2ObjectOpenHashMap<>(); + Int2ObjectOpenHashMap>> minmaxs = new Int2ObjectOpenHashMap<>(); + for (int fortune = 0; fortune < 4; fortune++) { + ItemStack tool = new ItemStack(Items.DIAMOND_PICKAXE); + if (fortune > 0) { + tool.addEnchantment(Enchantments.FORTUNE, fortune); + } + TileEntity tile = null; + try { + tile = state.createTileEntity(world); + } catch (Exception e) { + } + player.setItemStackToSlot(EquipmentSlotType.MAINHAND, tool); + LootContext.Builder builder = new LootContext.Builder(world)// + .withParameter(LootParameters.POSITION, BlockPos.ZERO)// + .withParameter(LootParameters.BLOCK_STATE, state)// + .withNullableParameter(LootParameters.BLOCK_ENTITY, tile)// + .withNullableParameter(LootParameters.THIS_ENTITY, player)// + .withParameter(LootParameters.TOOL, tool); + Object2IntOpenCustomHashMap stacks = new Object2IntOpenCustomHashMap<>(strategy); + Object2ObjectOpenCustomHashMap> minmax = new Object2ObjectOpenCustomHashMap<>( + strategy); + for (int i = 0; i < iteration; i++) { + NonNullList drops = NonNullList.create(); + drops.addAll(state.getDrops(builder)); + if (!eventCrashed) { + try { + ForgeEventFactory + .fireBlockHarvesting(drops, world, BlockPos.ZERO, state, fortune, 1F, false, + player); + } catch (Exception e) { + eventCrashed = true; + } + } + drops.removeIf(ItemStack::isEmpty); + for (ItemStack drop : drops) { + if (all.get() || drop.getItem() != show.getItem()) { + stacks.addTo(drop, drop.getCount()); + } + /*minmax.compute(drop, (s, p) -> { + if (p == null) { + return MutablePair.of(drop.getCount(), drop.getCount()); + } + p.setLeft(Math.min(drop.getCount(), p.getLeft())); + p.setRight(Math.max(drop.getCount(), p.getRight())); + return p; + });*/ + minmax.merge(drop, MutablePair.of(drop.getCount(), drop.getCount()), (pOld, pNew) -> { + pOld.setLeft(Math.min(pNew.getLeft(), pOld.getLeft())); + pOld.setRight(Math.max(pNew.getRight(), pOld.getLeft())); + return pOld; + }); + } + } + minmaxs.put(fortune, minmax); + resultMap.put(fortune, stacks); + } + ObjectOpenCustomHashSet allStacks = new ObjectOpenCustomHashSet<>(strategy); + resultMap.values().forEach(map -> allStacks.addAll(map.keySet())); + allStacks.stream()// + .sorted(Comparator.comparingInt(s -> Item.getIdFromItem(s.getItem())))// + .forEach(s -> { + Drop drop = new Drop(s); + for (int i = 0; i < 4; i++) { + drop.getChances().put(i, resultMap.get(i).getInt(s) / (float) iteration); + drop.getMaxs().put(i, minmaxs.get(i).get(s).getRight().intValue()); + drop.getMins().put(i, minmaxs.get(i).get(s).getLeft().intValue()); + } + result.add(drop); + }); + return result; + } catch (Exception e) { + return Collections.emptyList(); + } + } + + @SubscribeEvent + public void serverStart(FMLServerStartingEvent event) { + List allRecipes = getAllRecipes( + ModList.get().getMods().stream().map(ModInfo::getModId).filter(s -> !blacklistedMods.get().contains(s)) + .collect(Collectors.toSet()), event); + allRecipes.forEach(System.out::println); + allRecipes.hashCode(); + + } + +} diff --git a/src/main/java/kdp/blockdrops/Category.java b/src/main/java/kdp/blockdrops/Category.java new file mode 100644 index 0000000..129fb8a --- /dev/null +++ b/src/main/java/kdp/blockdrops/Category.java @@ -0,0 +1,76 @@ +package kdp.blockdrops; + +import java.util.List; + +import net.minecraft.client.Minecraft; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.client.config.GuiUtils; + +import mezz.jei.api.constants.VanillaTypes; +import mezz.jei.api.gui.IRecipeLayout; +import mezz.jei.api.gui.drawable.IDrawable; +import mezz.jei.api.gui.ingredient.IGuiItemStackGroup; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.category.IRecipeCategory; +import mezz.jei.gui.elements.DrawableBlank; + +public class Category implements IRecipeCategory { + private static final ResourceLocation hopper = new ResourceLocation("textures/gui/container/hopper.png"); + + @Override + public ResourceLocation getUid() { + return BlockDrops.RL; + } + + @Override + public Class getRecipeClass() { + return DropRecipe.class; + } + + @Override + public String getTitle() { + return "Block Drops"; + } + + @Override + public IDrawable getBackground() { + return new DrawableBlank(180, 40); + } + + @Override + public IDrawable getIcon() { + return null; + } + + @Override + public void draw(DropRecipe recipe, double mouseX, double mouseY) { + Minecraft.getInstance().fontRenderer.drawStringWithShadow("Albatros", 10, 10, 0xFFFFFF00); + drawSlot(81, 1); + for (int i = 9; i < 170; i += 18) + drawSlot(i, 20); + } + + private void drawSlot(int x, int y) { + Minecraft.getInstance().getTextureManager().bindTexture(hopper); + GuiUtils.drawTexturedModalRect(x, y, 43, 19, 18, 18, 0); + } + + @Override + public void setIngredients(DropRecipe recipe, IIngredients ingredients) { + ingredients.setInputs(VanillaTypes.ITEM, recipe.getInputs()); + ingredients.setOutputs(VanillaTypes.ITEM, recipe.getOutputs()); + } + + @Override + public void setRecipe(IRecipeLayout recipeLayout, DropRecipe recipe, IIngredients ingredients) { + IGuiItemStackGroup stackGroup = recipeLayout.getItemStacks(); + stackGroup.init(0, true, 81, 1); + stackGroup.set(0, recipe.getInputs()); + stackGroup.addTooltipCallback((int slotIndex, boolean input, ItemStack ingredient, List tooltip) -> { + if (!input) { + tooltip.add("NICE"); + } + }); + } +} diff --git a/src/main/java/kdp/blockdrops/Drop.java b/src/main/java/kdp/blockdrops/Drop.java new file mode 100644 index 0000000..6ac07fd --- /dev/null +++ b/src/main/java/kdp/blockdrops/Drop.java @@ -0,0 +1,82 @@ +package kdp.blockdrops; + +import java.util.Arrays; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.IntArrayNBT; +import net.minecraftforge.common.util.INBTSerializable; + +import it.unimi.dsi.fastutil.ints.Int2FloatLinkedOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap; + +public class Drop implements INBTSerializable { + private ItemStack out; + private final Int2FloatLinkedOpenHashMap chances = new Int2FloatLinkedOpenHashMap(); + private final Int2IntLinkedOpenHashMap mins = new Int2IntLinkedOpenHashMap(); + private final Int2IntLinkedOpenHashMap maxs = new Int2IntLinkedOpenHashMap(); + + public Drop(ItemStack out) { + this.out = out; + } + + Drop() { + } + + public ItemStack getOut() { + return out; + } + + public Int2FloatLinkedOpenHashMap getChances() { + return chances; + } + + public Int2IntLinkedOpenHashMap getMins() { + return mins; + } + + public Int2IntLinkedOpenHashMap getMaxs() { + return maxs; + } + + @Override + public String toString() { + return out.getItem().getRegistryName().toString(); + } + + @Override + public CompoundNBT serializeNBT() { + CompoundNBT nbt = new CompoundNBT(); + nbt.put("out", out.write(new CompoundNBT())); + IntArrayNBT ck = new IntArrayNBT(chances.keySet().toIntArray()); + IntArrayNBT cv = new IntArrayNBT(chances.values().stream().mapToInt(Float::floatToRawIntBits).toArray()); + IntArrayNBT nk = new IntArrayNBT(mins.keySet().toIntArray()); + IntArrayNBT nv = new IntArrayNBT(mins.values().toIntArray()); + IntArrayNBT xk = new IntArrayNBT(maxs.keySet().toIntArray()); + IntArrayNBT xv = new IntArrayNBT(maxs.values().toIntArray()); + nbt.put("ck", ck); + nbt.put("cv", cv); + nbt.put("nk", nk); + nbt.put("nv", nv); + nbt.put("xk", xk); + nbt.put("xv", xv); + return nbt; + } + + @Override + public void deserializeNBT(CompoundNBT nbt) { + out = ItemStack.read(nbt.getCompound("out")); + int[] ck = nbt.getIntArray("ck"); + Float[] cv = Arrays.stream(nbt.getIntArray("cv")).mapToObj(Float::intBitsToFloat).toArray(Float[]::new); + int[] nk = nbt.getIntArray("nk"); + int[] nv = nbt.getIntArray("nv"); + int[] xk = nbt.getIntArray("xk"); + int[] xv = nbt.getIntArray("xv"); + for (int i = 0; i < 4; i++) { + chances.put(ck[i], cv[i].floatValue()); + mins.put(nk[i], nv[i]); + maxs.put(xk[i], xv[i]); + } + + } +} diff --git a/src/main/java/kdp/blockdrops/DropRecipe.java b/src/main/java/kdp/blockdrops/DropRecipe.java new file mode 100644 index 0000000..a5ba7a7 --- /dev/null +++ b/src/main/java/kdp/blockdrops/DropRecipe.java @@ -0,0 +1,62 @@ +package kdp.blockdrops; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraftforge.common.util.INBTSerializable; + +public class DropRecipe implements INBTSerializable { + + private ItemStack in; + private List drops; + + public DropRecipe(ItemStack in, List drops) { + this.in = in; + this.drops = drops; + } + + public DropRecipe() { + } + + public List getInputs() { + return Collections.singletonList(in); + } + + public List getOutputs() { + return drops.stream().map(Drop::getOut).collect(Collectors.toList()); + } + + public void setIn(ItemStack in) { + this.in = in; + } + + public void setDrops(List drops) { + this.drops = drops; + } + + @Override + public CompoundNBT serializeNBT() { + CompoundNBT nbt = new CompoundNBT(); + nbt.put("in", in.write(new CompoundNBT())); + ListNBT listNBT = new ListNBT(); + drops.forEach(d -> listNBT.add(d.serializeNBT())); + nbt.put("drops", listNBT); + return nbt; + } + + @Override + public void deserializeNBT(CompoundNBT nbt) { + in = ItemStack.read(nbt.getCompound("in")); + drops = new ArrayList<>(); + nbt.getList("drops", 10).forEach(n -> { + Drop d = new Drop(); + d.deserializeNBT((CompoundNBT) n); + drops.add(d); + }); + } +} diff --git a/src/main/java/kdp/blockdrops/Plugin.java b/src/main/java/kdp/blockdrops/Plugin.java new file mode 100644 index 0000000..11acb27 --- /dev/null +++ b/src/main/java/kdp/blockdrops/Plugin.java @@ -0,0 +1,41 @@ +package kdp.blockdrops; + +import java.util.Collections; +import java.util.Comparator; + +import net.minecraft.item.ItemStack; +import net.minecraft.item.PickaxeItem; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.registries.ForgeRegistries; + +import mezz.jei.api.IModPlugin; +import mezz.jei.api.JeiPlugin; +import mezz.jei.api.registration.IRecipeCatalystRegistration; +import mezz.jei.api.registration.IRecipeCategoryRegistration; +import mezz.jei.api.registration.IRecipeRegistration; + +@JeiPlugin +public class Plugin implements IModPlugin { + @Override + public ResourceLocation getPluginUid() { + return BlockDrops.RL; + } + + @Override + public void registerCategories(IRecipeCategoryRegistration registration) { + registration.addRecipeCategories(new Category()); + } + + @Override + public void registerRecipes(IRecipeRegistration registration) { + registration.addRecipes(Collections.emptyList(), BlockDrops.RL); + } + + @Override + public void registerRecipeCatalysts(IRecipeCatalystRegistration registration) { + ForgeRegistries.ITEMS.getValues().stream()// + .filter(i -> i instanceof PickaxeItem && i.getRegistryName().getNamespace().equals("minecraft"))// + .sorted(Comparator.comparingInt(i -> ((PickaxeItem) i).getTier().getMaxUses()))// + .forEach(i -> registration.addRecipeCatalyst(new ItemStack(i), BlockDrops.RL)); + } +} diff --git a/src/main/java/mrriegel/blockdrops/BlockDrops.java b/src/main/java/mrriegel/blockdrops/BlockDrops.java deleted file mode 100644 index 75a217c..0000000 --- a/src/main/java/mrriegel/blockdrops/BlockDrops.java +++ /dev/null @@ -1,369 +0,0 @@ -package mrriegel.blockdrops; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.Writer; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -import org.apache.commons.lang3.mutable.MutableBoolean; -import org.apache.commons.lang3.tuple.Pair; -import org.lwjgl.input.Keyboard; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.google.common.reflect.TypeToken; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import mezz.jei.gui.recipes.RecipeLayout; -import mezz.jei.gui.recipes.RecipesGui; -import mrriegel.blockdrops.util.Drop; -import mrriegel.blockdrops.util.FakeClientPlayer; -import mrriegel.blockdrops.util.FakeClientWorld; -import mrriegel.blockdrops.util.StackWrapper; -import mrriegel.blockdrops.util.WrapperJson; -import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; -import net.minecraft.client.Minecraft; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.util.NonNullList; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; -import net.minecraftforge.client.event.GuiScreenEvent.KeyboardInputEvent; -import net.minecraftforge.common.config.Configuration; -import net.minecraftforge.fml.common.Loader; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber; -import net.minecraftforge.fml.common.Mod.EventHandler; -import net.minecraftforge.fml.common.Mod.Instance; -import net.minecraftforge.fml.common.ProgressManager; -import net.minecraftforge.fml.common.ProgressManager.ProgressBar; -import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.registry.ForgeRegistries; -import net.minecraftforge.fml.relauncher.ReflectionHelper; - -@Mod(modid = BlockDrops.MODID, name = BlockDrops.MODNAME, version = BlockDrops.VERSION, acceptedMinecraftVersions = "[1.12,1.13)", dependencies = "required-after:jei@[4.8.0,);", clientSideOnly = true) -@EventBusSubscriber -public class BlockDrops { - public static final String MODID = "blockdrops"; - public static final String VERSION = "1.4.0"; - public static final String MODNAME = "Block Drops"; - - @Instance(BlockDrops.MODID) - public static BlockDrops instance; - - public static boolean all, showChance, showMinMax, multithreaded; - public static int iteration; - public static Set blacklist; - - public static List recipeWrappers; - public static Gson gson; - - private File recipeWrapFile, modHashFile; - - @EventHandler - public void preInit(FMLPreInitializationEvent event) { - File configDir = new File(event.getModConfigurationDirectory(), "BlockDrops"); - recipeWrapFile = new File(configDir, "blockdrops.txt"); - modHashFile = new File(configDir, "modVersions.txt"); - Configuration config = new Configuration(new File(configDir, "config.cfg")); - config.load(); - all = config.getBoolean("allDrops", Configuration.CATEGORY_CLIENT, false, "Show block drops of any block."); - showChance = config.getBoolean("showChance", Configuration.CATEGORY_CLIENT, true, "Show chance of drops."); - showMinMax = config.getBoolean("showMinMax", Configuration.CATEGORY_CLIENT, true, "Show minimum and maximum of drops."); - multithreaded = config.getBoolean("multithreaded", Configuration.CATEGORY_CLIENT, true, "Multithreaded calculation of drops"); - iteration = config.getInt("iteration", Configuration.CATEGORY_CLIENT, 4000, 1, 99999, "Number of calculation. The higher the more precise the chance."); - blacklist = Sets.newHashSet(config.getStringList("blacklist", Configuration.CATEGORY_CLIENT, new String[] { "flatcoloredblocks", "chisel", "xtones", "wallpapercraft", "sonarcore", "microblockcbe" }, "Mod IDs of mods that won't be scanned.")); - if (config.hasChanged()) - config.save(); - gson = new GsonBuilder().setPrettyPrinting().registerTypeAdapter(Wrapper.class, new WrapperJson()).create(); - } - - @EventHandler - public void postInit(FMLPostInitializationEvent event) throws IOException { - if (recipeWrapFile.exists()) { - recipeWrappers = gson.fromJson(new BufferedReader(new FileReader(recipeWrapFile)), new TypeToken>() { - }.getType()); - if (recipeWrappers == null) - recipeWrappers = new ArrayList<>(); - } else { - recipeWrappers = Lists.newArrayList(); - recipeWrapFile.createNewFile(); - modHashFile.createNewFile(); - } - Map mods = Maps.newHashMap(); - Loader.instance().getActiveModList().forEach(m -> mods.put(m.getModId(), m.getVersion())); - for (String black : blacklist) - mods.remove(black); - Map fileMods = null; - if (modHashFile.exists()) { - fileMods = gson.fromJson(new BufferedReader(new FileReader(modHashFile)), new TypeToken>() { - }.getType()); - if (fileMods == null) - fileMods = Maps.newHashMap(); - } else { - modHashFile.createNewFile(); - fileMods = Maps.newHashMap(); - } - Set check = Sets.newHashSet(); - for (Map.Entry entry : mods.entrySet()) { - if (!fileMods.containsKey(entry.getKey()) || !fileMods.get(entry.getKey()).equals(entry.getValue())) - check.add(entry.getKey()); - // else { - // if (!fileMods.get(entry.getKey()).equals(entry.getValue())) - // check.add(entry.getKey()); - // } - } - if (!check.isEmpty()) { - recipeWrappers.removeIf(w -> check.contains(w.getIn().getItem().getRegistryName().getResourceDomain())); - recipeWrappers.addAll(Lists.newArrayList(getRecipes(check))); - } - recipeWrappers.removeIf(rw -> rw.getIn().isEmpty()); - recipeWrappers.forEach(rw -> rw.getOut().removeIf(d -> d.out.isEmpty())); - - Writer fw = new BufferedWriter(new FileWriter(modHashFile)); - fw.write(gson.toJson(mods)); - fw.close(); - fw = new BufferedWriter(new FileWriter(recipeWrapFile)); - fw.write(gson.toJson(recipeWrappers)); - fw.close(); - } - - public static List getRecipes(Collection ids) { - List res = Lists.newArrayList(); - Set stateSet = Sets.newHashSet(); - for (Block b : ForgeRegistries.BLOCKS) { - if (!ids.contains(b.getRegistryName().getResourceDomain()) || Item.getItemFromBlock(b) == null) - continue; - NonNullList lis = NonNullList.create(); - b.getSubBlocks(b.getCreativeTabToDisplayOn(), lis); - try { - List stacks = Lists.newArrayList(); - for (int i = 0; i < 16; i++) { - IBlockState st = b.getStateFromMeta(i); - for (ItemStack s : lis) - if (!s.isEmpty() && s.isItemEqual(getStack(st)) && !stacks.stream().anyMatch(ss -> ss.isItemEqual(s))) { - stateSet.add(st); - stacks.add(s); - } - } - } catch (Exception e) { - } - } - List states = Lists.newArrayList(stateSet); - states.sort((IBlockState o1, IBlockState o2) -> { - int id = Integer.compare(Block.getIdFromBlock(o1.getBlock()), Block.getIdFromBlock(o2.getBlock())); - int meta = Integer.compare(o1.getBlock().getMetaFromState(o1), o2.getBlock().getMetaFromState(o2)); - return id != 0 ? id : meta; - }); - - ProgressManager.ProgressBar bar = ProgressManager.push("Analysing Drops", states.size()); - ExecutorService threadPool = Executors.newCachedThreadPool(); // Pool to store all threads - Consumer task = (st) -> { - List drops; - bar.step(st.getBlock().getRegistryName().toString()); - try { - drops = getList(st); - } catch (Throwable e) { - // BlockDrops.logger.error("An error occured while calculating drops for " + st.getBlock().getLocalizedName() + " (" + e.getClass() + ")"); - drops = Collections.emptyList(); - } - if (drops.isEmpty()) - return; - res.add(new Wrapper(getStack(st), drops)); - }; - for (IBlockState st : states) { - if (multithreaded) { - threadPool.execute(() -> task.accept(st)); - } else - task.accept(st); - } - threadPool.shutdown(); - try { - threadPool.awaitTermination(1, TimeUnit.HOURS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if (bar.getSteps() != bar.getStep()) - ReflectionHelper.setPrivateValue(ProgressBar.class, bar, bar.getSteps(), "step"); - ProgressManager.pop(bar); - return res; - - } - - private static List getList(IBlockState state) { - List drops = Lists.newArrayList(); - if (getStack(state).isEmpty()) - return drops; - List stacks0 = Lists.newArrayList(), stacks1 = Lists.newArrayList(), stacks2 = Lists.newArrayList(), stacks3 = Lists.newArrayList(); - Map> pairs0 = Maps.newHashMap(), pairs1 = Maps.newHashMap(), pairs2 = Maps.newHashMap(), pairs3 = Maps.newHashMap(); - boolean crashed = false; - for (int i = 0; i < BlockDrops.iteration; i++) { - for (int j = 0; j < 4; j++) { - List lis = Lists.newArrayList(state.getBlock().getDrops(FakeClientWorld.getInstance(), BlockPos.ORIGIN, state, j)); - if (!crashed) - try { - net.minecraftforge.event.ForgeEventFactory.fireBlockHarvesting(lis, FakeClientWorld.getInstance(), BlockPos.ORIGIN, state, j, 1f, false, FakeClientPlayer.getInstance()); - } catch (Throwable t) { - crashed = true; - } - lis.removeIf(ItemStack::isEmpty); - switch (j) { - case 0: - add(pairs0, lis); - break; - case 1: - add(pairs1, lis); - break; - case 2: - add(pairs2, lis); - break; - case 3: - add(pairs3, lis); - break; - } - for (ItemStack s : lis) { - switch (j) { - case 0: - add(stacks0, s); - break; - case 1: - add(stacks1, s); - break; - case 2: - add(stacks2, s); - break; - case 3: - add(stacks3, s); - break; - } - } - } - } - - List stacks = Lists.newArrayList(); - for (StackWrapper w : stacks0) - add(stacks, w.stack); - for (StackWrapper w : stacks1) - add(stacks, w.stack); - for (StackWrapper w : stacks2) - add(stacks, w.stack); - for (StackWrapper w : stacks3) - add(stacks, w.stack); - - if (!BlockDrops.all) { - stacks.removeIf(tmp -> tmp.stack.isItemEqual(getStack(state))); - } - stacks.sort((o1, o2) -> { - int id = Integer.compare(Item.getIdFromItem(o1.stack.getItem()), Item.getIdFromItem(o2.stack.getItem())); - int meta = Integer.compare(o1.stack.getItemDamage(), o2.stack.getItemDamage()); - return id != 0 ? id : meta; - }); - - for (int i = 0; i < stacks.size(); i++) { - StackWrapper stack = stacks.get(i); - float s0 = getChance(stacks0, stack.stack); - float s1 = getChance(stacks1, stack.stack); - float s2 = getChance(stacks2, stack.stack); - float s3 = getChance(stacks3, stack.stack); - if (!stack.stack.isEmpty()) - drops.add(new Drop(stack.stack, s0, s1, s2, s3, pairs0.get(stack), pairs1.get(stack), pairs2.get(stack), pairs3.get(stack))); - } - return drops; - - } - - private static float getChance(List stacks, ItemStack stack) { - if (!BlockDrops.showChance) - return 0f; - int con = contains(stacks, stack); - if (con == -1) - return 0F; - return 100F * ((float) stacks.get(con).size / (float) BlockDrops.iteration); - } - - private static int contains(List lis, ItemStack stack) { - for (int i = 0; i < lis.size(); i++) - if (lis.get(i).stack.isItemEqual(stack)) - return i; - return -1; - } - - private static void add(List lis, ItemStack stack) { - int con = contains(lis, stack); - if (con == -1) - lis.add(new StackWrapper(stack, stack.getCount())); - else { - StackWrapper tmp = lis.get(con); - tmp.size += stack.getCount(); - lis.set(con, tmp); - } - } - - private static void add(Map> map, List lis) { - List list = Lists.newArrayList(); - for (ItemStack s : lis) - add(list, s); - for (StackWrapper w : list) { - if (map.get(w) == null) - map.put(w, Pair.of(10000, 0)); - int min = map.get(w).getLeft(); - int max = map.get(w).getRight(); - Pair pair = Pair.of(Math.min(min, w.size), Math.max(max, w.size)); - map.put(w, pair); - } - } - - private static ItemStack getStack(IBlockState state) { - return state.getBlock().getPickBlock(state, new RayTraceResult(FakeClientPlayer.getInstance()), FakeClientWorld.getInstance(), BlockPos.ORIGIN, FakeClientPlayer.getInstance()); - } - - private static Field recipeLayouts, recipeWrapper; - - @SubscribeEvent - public static void key(KeyboardInputEvent.Post event) throws IllegalAccessException { - if (Minecraft.getMinecraft().currentScreen instanceof RecipesGui && Keyboard.getEventKeyState()) { - if (recipeLayouts == null) { - recipeLayouts = ReflectionHelper.findField(RecipesGui.class, "recipeLayouts"); - recipeWrapper = ReflectionHelper.findField(RecipeLayout.class, "recipeWrapper"); - } - MutableBoolean change = new MutableBoolean(false); - ((List) recipeLayouts.get(Minecraft.getMinecraft().currentScreen)).stream().map(rl -> { - try { - return recipeWrapper.get(rl); - } catch (IllegalArgumentException | IllegalAccessException e) { - return null; - } - }).filter(o -> o instanceof Wrapper).map(o -> (Wrapper) o)/*.collect(Collectors.toList())*/.forEach(w -> { - if (Keyboard.isKeyDown(Keyboard.KEY_LEFT)) { - w.decreaseIndex(); - change.setTrue(); - } else if (Keyboard.isKeyDown(Keyboard.KEY_RIGHT)) { - w.increaseIndex(); - change.setTrue(); - } - }); - if (change.isTrue()) - ((RecipesGui) Minecraft.getMinecraft().currentScreen).onStateChange(); - } - } - -} diff --git a/src/main/java/mrriegel/blockdrops/Category.java b/src/main/java/mrriegel/blockdrops/Category.java deleted file mode 100644 index 6dbd33f..0000000 --- a/src/main/java/mrriegel/blockdrops/Category.java +++ /dev/null @@ -1,60 +0,0 @@ -package mrriegel.blockdrops; - -import mezz.jei.api.gui.IDrawable; -import mezz.jei.api.gui.IGuiItemStackGroup; -import mezz.jei.api.gui.IRecipeLayout; -import mezz.jei.api.ingredients.IIngredients; -import mezz.jei.api.recipe.IRecipeCategory; -import mezz.jei.gui.elements.DrawableBlank; -import net.minecraft.client.Minecraft; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.client.config.GuiUtils; - -public class Category implements IRecipeCategory { - - private ResourceLocation hopper = new ResourceLocation("textures/gui/container/hopper.png"); - - @Override - public String getUid() { - return BlockDrops.MODID; - } - - @Override - public String getTitle() { - return "Block Drops"; - } - - @Override - public IDrawable getBackground() { - return new DrawableBlank(180, 40); - } - - @Override - public void drawExtras(Minecraft minecraft) { - drawSlot(81, 1); - for (int i = 9; i < 170; i += 18) - drawSlot(i, 20); - } - - private void drawSlot(int x, int y) { - Minecraft.getMinecraft().getTextureManager().bindTexture(hopper); - GuiUtils.drawTexturedModalRect(x, y, 43, 19, 18, 18, 0); - } - - @Override - public void setRecipe(IRecipeLayout recipeLayout, Wrapper recipeWrapper, IIngredients ingredients) { - IGuiItemStackGroup itemStacks = recipeLayout.getItemStacks(); - itemStacks.init(0, true, 81, 1); - itemStacks.set(0, recipeWrapper.getInputs()); - itemStacks.addTooltipCallback(recipeWrapper); - for (int i = 0; i < Math.min(recipeWrapper.getOutputs().size(), 9); i++) { - itemStacks.init(i + 1, false, 9 + i * 18, 20); - itemStacks.set(i + 1, recipeWrapper.getOutputs().get((i + recipeWrapper.getIndex()))); - } - } - - @Override - public String getModName() { - return BlockDrops.MODNAME; - } -} \ No newline at end of file diff --git a/src/main/java/mrriegel/blockdrops/Plugin.java b/src/main/java/mrriegel/blockdrops/Plugin.java deleted file mode 100644 index 108a388..0000000 --- a/src/main/java/mrriegel/blockdrops/Plugin.java +++ /dev/null @@ -1,29 +0,0 @@ -package mrriegel.blockdrops; - -import mezz.jei.api.IModPlugin; -import mezz.jei.api.IModRegistry; -import mezz.jei.api.JEIPlugin; -import mezz.jei.api.recipe.IRecipeCategoryRegistration; -import net.minecraft.item.Item; -import net.minecraft.item.ItemPickaxe; -import net.minecraft.item.ItemStack; -import net.minecraftforge.fml.common.registry.ForgeRegistries; - -@JEIPlugin -public class Plugin implements IModPlugin { - @Override - public void register(IModRegistry registry) { - registry.handleRecipes(Wrapper.class, r -> r, BlockDrops.MODID); - registry.addRecipes(BlockDrops.recipeWrappers, BlockDrops.MODID); - for (Item i : ForgeRegistries.ITEMS) { - if (i instanceof ItemPickaxe) - registry.addRecipeCatalyst(new ItemStack(i), BlockDrops.MODID); - } - } - - @Override - public void registerCategories(IRecipeCategoryRegistration registry) { - registry.addRecipeCategories(new Category()); - } - -} diff --git a/src/main/java/mrriegel/blockdrops/Wrapper.java b/src/main/java/mrriegel/blockdrops/Wrapper.java deleted file mode 100644 index 578f254..0000000 --- a/src/main/java/mrriegel/blockdrops/Wrapper.java +++ /dev/null @@ -1,137 +0,0 @@ -package mrriegel.blockdrops; - -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import org.apache.commons.lang3.tuple.MutablePair; -import org.apache.commons.lang3.tuple.Pair; - -import mezz.jei.api.gui.ITooltipCallback; -import mezz.jei.api.ingredients.IIngredients; -import mezz.jei.api.recipe.IRecipeWrapper; -import mrriegel.blockdrops.util.Drop; -import net.minecraft.client.resources.I18n; -import net.minecraft.item.ItemStack; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.text.TextFormatting; - -public class Wrapper implements IRecipeWrapper, ITooltipCallback { - - private ItemStack in; - private List out; - private int index = 0, maxIndex; - - public Wrapper(ItemStack in, List out) { - this.in = in; - this.out = out; - this.maxIndex = Math.max(0, out.size() - 9); - } - - public List getInputs() { - return Collections.singletonList(in); - } - - public List getOutputs() { - return out.stream().map(d -> d.out).collect(Collectors.toList()); - } - - private float chance(ItemStack s, int fortune) { - for (Drop d : out) - if (d.out.isItemEqual(s)) { - switch (fortune) { - case 0: - return d.chance0; - case 1: - return d.chance1; - case 2: - return d.chance2; - case 3: - return d.chance3; - default: - break; - } - } - return 0f; - } - - @Override - public String toString() { - return "Wrapper [in=" + in + ", out=" + out + "]"; - } - - private Pair pair(ItemStack s, int fortune) { - for (Drop d : out) - if (d.out.isItemEqual(s)) { - switch (fortune) { - case 0: - return d.pair0; - case 1: - return d.pair1; - case 2: - return d.pair2; - case 3: - return d.pair3; - default: - break; - } - } - return MutablePair.of(0, 0); - } - - @Override - public void onTooltip(int slotIndex, boolean input, ItemStack ingredient, List tooltip) { - if (!input) { - boolean one = IntStream.range(0, 4).mapToObj(i -> Float.floatToIntBits(chance(ingredient, i))).distinct().count() == 1; - for (int x = 0; x < (one ? 1 : 4); x++) { - String chance = BlockDrops.showChance ? String.format("%.2f", chance(ingredient, (int) x)) + " % " : ""; - String minmax = BlockDrops.showMinMax ? "Min: " + pair(ingredient, (int) x).getLeft() + " Max: " + pair(ingredient, (int) x).getRight() : ""; - if (BlockDrops.showChance || BlockDrops.showMinMax) - tooltip.add((one ? "" : TextFormatting.BLUE + "Fortune " + (0l != x ? I18n.format("enchantment.level." + x) : 0) + " ") + TextFormatting.GRAY + chance + minmax); - } - if (out.size() > 9) - tooltip.add(TextFormatting.RED + "There are too many possible drops. Use left and right key to cycle."); - } - } - - public ItemStack getIn() { - return in; - } - - public void setIn(ItemStack in) { - this.in = in; - } - - public List getOut() { - return out; - } - - public void setOut(List out) { - this.out = out; - this.maxIndex = Math.max(0, out.size() - 9); - } - - @Override - public void getIngredients(IIngredients ingredients) { - ingredients.setInputs(ItemStack.class, getInputs()); - ingredients.setOutputs(ItemStack.class, getOutputs()); - } - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = MathHelper.clamp(index, 0, maxIndex); - } - - public void increaseIndex() { - this.index = MathHelper.clamp(index + 1, 0, maxIndex); - } - - public void decreaseIndex() { - this.index = MathHelper.clamp(index - 1, 0, maxIndex); - } - -} \ No newline at end of file diff --git a/src/main/java/mrriegel/blockdrops/util/Drop.java b/src/main/java/mrriegel/blockdrops/util/Drop.java deleted file mode 100644 index 2a27a77..0000000 --- a/src/main/java/mrriegel/blockdrops/util/Drop.java +++ /dev/null @@ -1,69 +0,0 @@ -package mrriegel.blockdrops.util; - -import org.apache.commons.lang3.tuple.Pair; - -import net.minecraft.item.ItemStack; -import net.minecraftforge.items.ItemHandlerHelper; - -public class Drop { - public ItemStack out; - public float chance0, chance1, chance2, chance3; - public Pair pair0, pair1, pair2, pair3; - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Drop other = (Drop) obj; - if (Float.floatToIntBits(chance0) != Float.floatToIntBits(other.chance0)) - return false; - if (Float.floatToIntBits(chance1) != Float.floatToIntBits(other.chance1)) - return false; - if (Float.floatToIntBits(chance2) != Float.floatToIntBits(other.chance2)) - return false; - if (Float.floatToIntBits(chance3) != Float.floatToIntBits(other.chance3)) - return false; - if (out == null) { - if (other.out != null) - return false; - } else if (!out.isItemEqual(other.out)) - return false; - return true; - } - - public Drop(ItemStack out, float chance0, float chance1, float chance2, float chance3, Pair pair0, Pair pair1, Pair pair2, Pair pair3) { - super(); - this.out = ItemHandlerHelper.copyStackWithSize(out, 1); - this.chance0 = chance0; - this.chance1 = chance1; - this.chance2 = chance2; - this.chance3 = chance3; - - this.pair0 = pair0; - if (this.pair0 == null) - this.pair0 = Pair.of(0, 0); - this.pair1 = pair1; - if (this.pair1 == null) - this.pair1 = Pair.of(0, 0); - this.pair2 = pair2; - if (this.pair2 == null) - this.pair2 = Pair.of(0, 0); - this.pair3 = pair3; - if (this.pair3 == null) - this.pair3 = Pair.of(0, 0); - - if (this.chance0 < 100) - this.pair0 = Pair.of(0, this.pair0.getRight()); - if (this.chance1 < 100) - this.pair1 = Pair.of(0, this.pair1.getRight()); - if (this.chance2 < 100) - this.pair2 = Pair.of(0, this.pair2.getRight()); - if (this.chance3 < 100) - this.pair3 = Pair.of(0, this.pair3.getRight()); - } - -} diff --git a/src/main/java/mrriegel/blockdrops/util/FakeClientPlayer.java b/src/main/java/mrriegel/blockdrops/util/FakeClientPlayer.java deleted file mode 100644 index 84fb123..0000000 --- a/src/main/java/mrriegel/blockdrops/util/FakeClientPlayer.java +++ /dev/null @@ -1,40 +0,0 @@ -package mrriegel.blockdrops.util; - -import java.util.UUID; - -import javax.annotation.Nullable; - -import com.mojang.authlib.GameProfile; - -import net.minecraft.entity.player.EntityPlayer; - -/** - * - * @author mezz - * - */ -public class FakeClientPlayer extends EntityPlayer { - @Nullable - private static FakeClientPlayer INSTANCE; - - public static FakeClientPlayer getInstance() { - if (INSTANCE == null) { - INSTANCE = new FakeClientPlayer(); - } - return INSTANCE; - } - - private FakeClientPlayer() { - super(FakeClientWorld.getInstance(), new GameProfile(new UUID(0, 0), "JEI_Fake")); - } - - @Override - public boolean isSpectator() { - return false; - } - - @Override - public boolean isCreative() { - return false; - } -} \ No newline at end of file diff --git a/src/main/java/mrriegel/blockdrops/util/FakeClientWorld.java b/src/main/java/mrriegel/blockdrops/util/FakeClientWorld.java deleted file mode 100644 index 782e18b..0000000 --- a/src/main/java/mrriegel/blockdrops/util/FakeClientWorld.java +++ /dev/null @@ -1,98 +0,0 @@ -package mrriegel.blockdrops.util; - -import javax.annotation.Nullable; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.init.Blocks; -import net.minecraft.profiler.Profiler; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.DimensionType; -import net.minecraft.world.GameType; -import net.minecraft.world.World; -import net.minecraft.world.WorldProvider; -import net.minecraft.world.WorldSettings; -import net.minecraft.world.WorldType; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.chunk.EmptyChunk; -import net.minecraft.world.chunk.IChunkProvider; -import net.minecraft.world.storage.ISaveHandler; -import net.minecraft.world.storage.SaveDataMemoryStorage; -import net.minecraft.world.storage.SaveHandlerMP; -import net.minecraft.world.storage.WorldInfo; - -/** - * - * @author mezz - * - */ -public class FakeClientWorld extends World { - private static final WorldSettings worldSettings = new WorldSettings(0, GameType.SURVIVAL, false, false, WorldType.DEFAULT); - private static final WorldInfo worldInfo = new WorldInfo(worldSettings, "jei_fake"); - private static final ISaveHandler saveHandler = new SaveHandlerMP(); - private static final WorldProvider worldProvider = new WorldProvider() { - @Override - public DimensionType getDimensionType() { - return DimensionType.OVERWORLD; - } - }; - @Nullable - private static FakeClientWorld INSTANCE; - - public static FakeClientWorld getInstance() { - if (INSTANCE == null) { - INSTANCE = new FakeClientWorld(); - } - return INSTANCE; - } - - private FakeClientWorld() { - super(saveHandler, worldInfo, worldProvider, new Profiler(), /*hacky*/false); - this.provider.setWorld(this); - this.mapStorage = new SaveDataMemoryStorage(); - } - - @Override - public BlockPos getSpawnPoint() { - return new BlockPos(0, 0, 0); - } - - @Override - public IBlockState getBlockState(BlockPos pos) { - return Blocks.AIR.getDefaultState(); - } - - @Override - protected IChunkProvider createChunkProvider() { - return new IChunkProvider() { - @Override - public Chunk getLoadedChunk(int x, int z) { - return new EmptyChunk(FakeClientWorld.this, x, z); - } - - @Override - public Chunk provideChunk(int x, int z) { - return new EmptyChunk(FakeClientWorld.this, x, z); - } - - @Override - public boolean tick() { - return false; - } - - @Override - public String makeString() { - return ""; - } - - @Override - public boolean isChunkGeneratedAt(int p_191062_1_, int p_191062_2_) { - return false; - } - }; - } - - @Override - protected boolean isChunkLoaded(int x, int z, boolean allowEmpty) { - return false; - } -} diff --git a/src/main/java/mrriegel/blockdrops/util/StackWrapper.java b/src/main/java/mrriegel/blockdrops/util/StackWrapper.java deleted file mode 100644 index 8acf9c9..0000000 --- a/src/main/java/mrriegel/blockdrops/util/StackWrapper.java +++ /dev/null @@ -1,33 +0,0 @@ -package mrriegel.blockdrops.util; - -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraftforge.items.ItemHandlerHelper; - -public class StackWrapper { - public ItemStack stack; - public int size; - - @Override - public boolean equals(Object obj) { - if (obj instanceof StackWrapper) - return ItemHandlerHelper.canItemStacksStack(stack, ((StackWrapper) obj).stack); - return false; - } - - public StackWrapper(ItemStack stack, int num) { - super(); - this.stack = stack; - this.size = num; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + Item.getIdFromItem(stack.getItem()); - result = prime * result + stack.getItemDamage(); - result = prime * result + (stack.getTagCompound() == null ? 0 : stack.getTagCompound().hashCode()); - return result; - } -} diff --git a/src/main/java/mrriegel/blockdrops/util/WrapperJson.java b/src/main/java/mrriegel/blockdrops/util/WrapperJson.java deleted file mode 100644 index e410ff5..0000000 --- a/src/main/java/mrriegel/blockdrops/util/WrapperJson.java +++ /dev/null @@ -1,86 +0,0 @@ -package mrriegel.blockdrops.util; - -import java.lang.reflect.Type; -import java.util.Collections; -import java.util.List; - -import org.apache.commons.lang3.tuple.MutablePair; - -import com.google.common.collect.Lists; -import com.google.common.reflect.TypeToken; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; - -import mrriegel.blockdrops.BlockDrops; -import mrriegel.blockdrops.Wrapper; -import net.minecraft.item.ItemStack; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.common.registry.ForgeRegistries; - -public class WrapperJson implements JsonDeserializer, JsonSerializer { - - @Override - public JsonElement serialize(Wrapper src, Type typeOfSrc, JsonSerializationContext context) { - JsonObject json = new JsonObject(); - ItemStack stack = src.getIn(); - json.addProperty("name", stack.getItem().getRegistryName().toString()); - json.addProperty("meta", stack.getItemDamage()); - json.addProperty("length", src.getOut().size()); - for (int i = 0; i < src.getOut().size(); i++) { - Drop d = src.getOut().get(i); - json.addProperty("name" + i, d.out.getItem().getRegistryName().toString()); - json.addProperty("meta" + i, d.out.getItemDamage()); - json.addProperty("0chance" + i, d.chance0); - json.addProperty("1chance" + i, d.chance1); - json.addProperty("2chance" + i, d.chance2); - json.addProperty("3chance" + i, d.chance3); - json.addProperty("0pair" + i, BlockDrops.gson.toJson(d.pair0)); - json.addProperty("1pair" + i, BlockDrops.gson.toJson(d.pair1)); - json.addProperty("2pair" + i, BlockDrops.gson.toJson(d.pair2)); - json.addProperty("3pair" + i, BlockDrops.gson.toJson(d.pair3)); - } - - return json; - } - - @SuppressWarnings("serial") - @Override - public Wrapper deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - Wrapper wrap = new Wrapper(null, Collections.EMPTY_LIST); - String name = json.getAsJsonObject().get("name").getAsString(); - int meta = json.getAsJsonObject().get("meta").getAsInt(); - ItemStack stack = new ItemStack(ForgeRegistries.ITEMS.getValue(new ResourceLocation(name)), 1, meta); - wrap.setIn(stack); - - int length = json.getAsJsonObject().get("length").getAsInt(); - List lis = Lists.newArrayList(); - for (int i = 0; i < length; i++) { - Drop d = new Drop(ItemStack.EMPTY, 0, 0, 0, 0, null, null, null, null); - String n = json.getAsJsonObject().get("name" + i).getAsString(); - int m = json.getAsJsonObject().get("meta" + i).getAsInt(); - ItemStack st = new ItemStack(ForgeRegistries.ITEMS.getValue(new ResourceLocation(n)), 1, m); - d.out = st; - d.chance0 = json.getAsJsonObject().get("0chance" + i).getAsFloat(); - d.chance1 = json.getAsJsonObject().get("1chance" + i).getAsFloat(); - d.chance2 = json.getAsJsonObject().get("2chance" + i).getAsFloat(); - d.chance3 = json.getAsJsonObject().get("3chance" + i).getAsFloat(); - d.pair0 = BlockDrops.gson.fromJson(json.getAsJsonObject().get("0pair" + i).getAsString(), new TypeToken>() { - }.getType()); - d.pair1 = BlockDrops.gson.fromJson(json.getAsJsonObject().get("1pair" + i).getAsString(), new TypeToken>() { - }.getType()); - d.pair2 = BlockDrops.gson.fromJson(json.getAsJsonObject().get("2pair" + i).getAsString(), new TypeToken>() { - }.getType()); - d.pair3 = BlockDrops.gson.fromJson(json.getAsJsonObject().get("3pair" + i).getAsString(), new TypeToken>() { - }.getType()); - lis.add(d); - } - wrap.setOut(lis); - return wrap; - } - -} diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..378b152 --- /dev/null +++ b/src/main/resources/META-INF/mods.toml @@ -0,0 +1,27 @@ +modLoader="javafml" +loaderVersion="[28,)" +issueTrackerURL="http://my.issue.tracker/" + +[[mods]] +modId="blockdrops" +version="1.0.0" +displayName="Block Drops" +displayURL="https://www.curseforge.com/minecraft/mc-mods/blockdrops" +credits="Thanks for this example mod goes to Java" +authors="KidsDontPlay" +description=''' + +''' + +[[dependencies.blockdrops]] + modId="forge" + mandatory=true + versionRange="[28,)" + ordering="NONE" + side="BOTH" +[[dependencies.blockdrops]] + modId="minecraft" + mandatory=true + versionRange="[1.14.4,1.15)" + ordering="NONE" + side="BOTH" diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info deleted file mode 100644 index e145db4..0000000 --- a/src/main/resources/mcmod.info +++ /dev/null @@ -1,16 +0,0 @@ -[ -{ - "modid": "blockdrops", - "name": "Block Drops", - "description": "Block Drops (JEI Addon)", - "version": "${version}", - "mcversion": "${mcversion}", - "url": "", - "updateUrl": "", - "authorList": ["MrRiegel"], - "credits": "The Forge and FML guys", - "logoFile": "", - "screenshots": [], - "dependencies": ["jei"] -} -] diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta new file mode 100644 index 0000000..1147d8a --- /dev/null +++ b/src/main/resources/pack.mcmeta @@ -0,0 +1,7 @@ +{ + "pack": { + "description": "blockdrops", + "pack_format": 4, + "_comment": "A pack_format of 4 requires json lang files. Note: we require v4 pack meta for all mods." + } +} From 6dd929ca51b663b06019aed028839cf9fee9ddce Mon Sep 17 00:00:00 2001 From: KidsDontPlay Date: Fri, 27 Sep 2019 21:52:27 +0200 Subject: [PATCH 2/9] booom --- src/main/java/kdp/blockdrops/BlockDrops.java | 5 +-- src/main/java/kdp/blockdrops/Category.java | 37 ++++++++++++++++++- .../java/kdp/blockdrops/ClientEvents.java | 21 +++++++++++ src/main/java/kdp/blockdrops/DropRecipe.java | 30 +++++++++++++++ src/main/java/kdp/blockdrops/Plugin.java | 29 +++++++++++++-- 5 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 src/main/java/kdp/blockdrops/ClientEvents.java diff --git a/src/main/java/kdp/blockdrops/BlockDrops.java b/src/main/java/kdp/blockdrops/BlockDrops.java index 9b45f29..cb0ef51 100644 --- a/src/main/java/kdp/blockdrops/BlockDrops.java +++ b/src/main/java/kdp/blockdrops/BlockDrops.java @@ -61,7 +61,7 @@ public class BlockDrops { public static final Logger LOG = LogManager.getLogger(BlockDrops.class); public static final ResourceLocation RL = new ResourceLocation(MOD_ID, "drops"); - private static ForgeConfigSpec.BooleanValue all, showChance, showMinMax, multithreaded; + public static ForgeConfigSpec.BooleanValue all, showChance, showMinMax, multithreaded; private static ForgeConfigSpec.IntValue iterations; private static ForgeConfigSpec.ConfigValue> blacklistedMods; @@ -212,12 +212,11 @@ private static List getDrops(BlockState state, FMLServerStartingEvent even @SubscribeEvent public void serverStart(FMLServerStartingEvent event) { + if(true)return; List allRecipes = getAllRecipes( ModList.get().getMods().stream().map(ModInfo::getModId).filter(s -> !blacklistedMods.get().contains(s)) .collect(Collectors.toSet()), event); allRecipes.forEach(System.out::println); - allRecipes.hashCode(); - } } diff --git a/src/main/java/kdp/blockdrops/Category.java b/src/main/java/kdp/blockdrops/Category.java index 129fb8a..98d7d1c 100644 --- a/src/main/java/kdp/blockdrops/Category.java +++ b/src/main/java/kdp/blockdrops/Category.java @@ -3,8 +3,10 @@ import java.util.List; import net.minecraft.client.Minecraft; +import net.minecraft.client.resources.I18n; import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.TextFormatting; import net.minecraftforge.fml.client.config.GuiUtils; import mezz.jei.api.constants.VanillaTypes; @@ -14,6 +16,7 @@ import mezz.jei.api.ingredients.IIngredients; import mezz.jei.api.recipe.category.IRecipeCategory; import mezz.jei.gui.elements.DrawableBlank; +import mezz.jei.gui.recipes.IRecipeLogicStateListener; public class Category implements IRecipeCategory { private static final ResourceLocation hopper = new ResourceLocation("textures/gui/container/hopper.png"); @@ -45,7 +48,6 @@ public IDrawable getIcon() { @Override public void draw(DropRecipe recipe, double mouseX, double mouseY) { - Minecraft.getInstance().fontRenderer.drawStringWithShadow("Albatros", 10, 10, 0xFFFFFF00); drawSlot(81, 1); for (int i = 9; i < 170; i += 18) drawSlot(i, 20); @@ -69,8 +71,39 @@ public void setRecipe(IRecipeLayout recipeLayout, DropRecipe recipe, IIngredient stackGroup.set(0, recipe.getInputs()); stackGroup.addTooltipCallback((int slotIndex, boolean input, ItemStack ingredient, List tooltip) -> { if (!input) { - tooltip.add("NICE"); + Drop d = recipe.getDropForItem(ingredient); + boolean one = d.getChances().values().stream().distinct().count() == 1; + for (int x = 0; x < (one ? 1 : 4); x++) { + String chance = BlockDrops.showChance.get() ? + String.format("%.2f", d.getChances().get(x) * 100F) + " % " : + ""; + String minmax = BlockDrops.showMinMax.get() ? + "Min: " + d.getMins().get(x) + " Max: " + d.getMaxs().get(x) : + ""; + if (!chance.isEmpty() || !minmax.isEmpty()) { + tooltip.add((one ? + "" : + TextFormatting.BLUE + "Fortune " + (0 != x ? + I18n.format("enchantment.level." + x) : + 0) + " ") + TextFormatting.GRAY + chance + minmax); + } + } + if (recipe.getOutputs().size() > 9) { + tooltip.add( + TextFormatting.RED + "There are too many possible drops. Use left and right key to cycle."); + } } }); + for (int i = 0; i < Math.min(recipe.getOutputs().size(), 9); i++) { + stackGroup.init(i + 1, false, 9 + i * 18, 20); + stackGroup.set(i + 1, recipe.getOutputs().get((i + recipe.getIndex()))); + } + } + + @Override + public boolean handleClick(DropRecipe recipe, double mouseX, double mouseY, int mouseButton) { + System.out.println(mouseX + " " + mouseY); + ((IRecipeLogicStateListener) Minecraft.getInstance().currentScreen).onStateChange(); + return false; } } diff --git a/src/main/java/kdp/blockdrops/ClientEvents.java b/src/main/java/kdp/blockdrops/ClientEvents.java new file mode 100644 index 0000000..061c25d --- /dev/null +++ b/src/main/java/kdp/blockdrops/ClientEvents.java @@ -0,0 +1,21 @@ +package kdp.blockdrops; + +import net.minecraft.client.Minecraft; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.InputEvent; +import net.minecraftforge.fml.common.Mod; + +import org.lwjgl.glfw.GLFW; + +import mezz.jei.api.runtime.IRecipesGui; + +@Mod.EventBusSubscriber(modid = BlockDrops.MOD_ID, value = Dist.CLIENT) +public class ClientEvents { + + public static void key(InputEvent.KeyInputEvent event) { + if (Minecraft.getInstance().currentScreen instanceof IRecipesGui && event.getAction() == GLFW.GLFW_RELEASE) { + boolean left = event.getKey() == GLFW.GLFW_KEY_LEFT; + boolean right = event.getKey() == GLFW.GLFW_KEY_RIGHT; + } + } +} diff --git a/src/main/java/kdp/blockdrops/DropRecipe.java b/src/main/java/kdp/blockdrops/DropRecipe.java index a5ba7a7..f134eb0 100644 --- a/src/main/java/kdp/blockdrops/DropRecipe.java +++ b/src/main/java/kdp/blockdrops/DropRecipe.java @@ -3,17 +3,25 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.ListNBT; +import net.minecraft.util.math.MathHelper; import net.minecraftforge.common.util.INBTSerializable; public class DropRecipe implements INBTSerializable { private ItemStack in; private List drops; + private Cache cache = CacheBuilder.newBuilder().build(); + //client only + private int index, maxIndex; public DropRecipe(ItemStack in, List drops) { this.in = in; @@ -39,6 +47,27 @@ public void setDrops(List drops) { this.drops = drops; } + public Drop getDropForItem(ItemStack stack) { + try { + return cache.get(stack, + () -> drops.stream().filter(drop -> drop.getOut().isItemEqual(stack)).findFirst().get()); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } + + public int getIndex() { + return this.index; + } + + public void increaseIndex() { + this.index = MathHelper.clamp(index + 1, 0, maxIndex); + } + + public void decreaseIndex() { + this.index = MathHelper.clamp(index - 1, 0, maxIndex); + } + @Override public CompoundNBT serializeNBT() { CompoundNBT nbt = new CompoundNBT(); @@ -58,5 +87,6 @@ public void deserializeNBT(CompoundNBT nbt) { d.deserializeNBT((CompoundNBT) n); drops.add(d); }); + maxIndex = Math.max(0, drops.size() - 9); } } diff --git a/src/main/java/kdp/blockdrops/Plugin.java b/src/main/java/kdp/blockdrops/Plugin.java index 11acb27..87854ae 100644 --- a/src/main/java/kdp/blockdrops/Plugin.java +++ b/src/main/java/kdp/blockdrops/Plugin.java @@ -1,8 +1,11 @@ package kdp.blockdrops; -import java.util.Collections; +import java.util.ArrayList; import java.util.Comparator; +import java.util.List; +import java.util.Random; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.PickaxeItem; import net.minecraft.util.ResourceLocation; @@ -28,14 +31,34 @@ public void registerCategories(IRecipeCategoryRegistration registration) { @Override public void registerRecipes(IRecipeRegistration registration) { - registration.addRecipes(Collections.emptyList(), BlockDrops.RL); + List list = new ArrayList<>(); + Random random = new Random(); + List items = new ArrayList<>(ForgeRegistries.ITEMS.getValues()); + for (int i = 0; i < 8; i++) { + DropRecipe a = new DropRecipe(); + list.add(a); + a.setIn(new ItemStack(items.get(random.nextInt(items.size())))); + List drops = new ArrayList<>(); + a.setDrops(drops); + for (int j = 0; j < random.nextInt(8) + 1; j++) { + Drop d = new Drop(new ItemStack(items.get(random.nextInt(items.size())))); + for (int k = 0; k < 4; k++) { + d.getChances().put(k, random.nextFloat()); + d.getMins().put(k, random.nextInt(5)); + d.getMaxs().put(k, random.nextInt(5)); + } + drops.add(d); + + } + } + registration.addRecipes(list, BlockDrops.RL); } @Override public void registerRecipeCatalysts(IRecipeCatalystRegistration registration) { ForgeRegistries.ITEMS.getValues().stream()// .filter(i -> i instanceof PickaxeItem && i.getRegistryName().getNamespace().equals("minecraft"))// - .sorted(Comparator.comparingInt(i -> ((PickaxeItem) i).getTier().getMaxUses()))// + .sorted(Comparator.comparingInt(i -> ((PickaxeItem) i).getTier().getMaxUses()).reversed())// .forEach(i -> registration.addRecipeCatalyst(new ItemStack(i), BlockDrops.RL)); } } From 97ca42aafe3172657f26a573a8f71655c83df70d Mon Sep 17 00:00:00 2001 From: KidsDontPlay Date: Sat, 5 Oct 2019 22:59:06 +0200 Subject: [PATCH 3/9] kar --- src/main/java/kdp/blockdrops/BlockDrops.java | 191 +++++++++++++++--- src/main/java/kdp/blockdrops/Category.java | 75 ++++++- .../java/kdp/blockdrops/ClientEvents.java | 21 -- src/main/java/kdp/blockdrops/Drop.java | 3 +- src/main/java/kdp/blockdrops/DropRecipe.java | 6 + src/main/java/kdp/blockdrops/Plugin.java | 28 +-- src/main/java/kdp/blockdrops/SyncMessage.java | 15 ++ 7 files changed, 254 insertions(+), 85 deletions(-) delete mode 100644 src/main/java/kdp/blockdrops/ClientEvents.java create mode 100644 src/main/java/kdp/blockdrops/SyncMessage.java diff --git a/src/main/java/kdp/blockdrops/BlockDrops.java b/src/main/java/kdp/blockdrops/BlockDrops.java index cb0ef51..5825d8d 100644 --- a/src/main/java/kdp/blockdrops/BlockDrops.java +++ b/src/main/java/kdp/blockdrops/BlockDrops.java @@ -1,5 +1,9 @@ package kdp.blockdrops; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -8,23 +12,34 @@ import java.util.List; import java.util.Set; import java.util.UUID; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import com.google.common.collect.ImmutableList; +import com.google.common.base.Stopwatch; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import com.mojang.authlib.GameProfile; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; import net.minecraft.enchantment.Enchantments; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.inventory.EquipmentSlotType; +import net.minecraft.item.BlockItem; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.JsonToNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.profiler.Profiler; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.NonNullList; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; import net.minecraft.world.dimension.DimensionType; import net.minecraft.world.server.ServerWorld; import net.minecraft.world.storage.loot.LootContext; @@ -32,7 +47,7 @@ import net.minecraftforge.common.ForgeConfigSpec; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.FakePlayerFactory; -import net.minecraftforge.event.ForgeEventFactory; +import net.minecraftforge.event.entity.EntityJoinWorldEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.ModLoadingContext; @@ -41,8 +56,14 @@ import net.minecraftforge.fml.event.server.FMLServerStartingEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.fml.loading.moddiscovery.ModInfo; +import net.minecraftforge.fml.network.NetworkRegistry; +import net.minecraftforge.fml.network.PacketDistributor; +import net.minecraftforge.fml.network.event.EventNetworkChannel; +import net.minecraftforge.fml.network.simple.SimpleChannel; import net.minecraftforge.registries.ForgeRegistries; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.tuple.MutablePair; import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; @@ -51,6 +72,8 @@ import it.unimi.dsi.fastutil.Hash; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; @@ -79,13 +102,24 @@ public boolean equals(ItemStack a, ItemStack b) { } }; + private static Path recipesPath; + + private static final String VERSION = "1.0"; + private static SimpleChannel simpleChannel = NetworkRegistry + .newSimpleChannel(new ResourceLocation(MOD_ID, "ch1"), () -> VERSION, VERSION::equals, VERSION::equals); + private static EventNetworkChannel eventNetworkChannel = NetworkRegistry + .newEventChannel(new ResourceLocation(MOD_ID, "ev"), () -> VERSION, VERSION::equals, VERSION::equals); + + private static List recipes = Collections.emptyList(); + private static Profiler profiler = new Profiler(System.nanoTime(), () -> 0); + public BlockDrops() { Pair pairCommon = new ForgeConfigSpec.Builder().configure(b -> { all = b.comment("Show block drops of any block").define("allBlocks", false); - multithreaded = b.comment("Multithreaded calculation of drops").define("multithreaded", true); + //multithreaded = b.comment("Multithreaded calculation of drops").define("multithreaded", true); iterations = b .comment("Amount of calculation iterations. The higher the more precise the calculation results") - .defineInRange("iterations", 4000, 1, 50000); + .defineInRange("iterations", 3000, 500, 20000); blacklistedMods = b.comment("Mod IDs of mods that won't be scanned").define("blacklistedMods", Arrays.asList("flatcoloredblocks", "chisel", "xtones", "wallpapercraft", "sonarcore", "microblockcbe")); @@ -100,26 +134,69 @@ public BlockDrops() { ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, pairClient.getValue()); MinecraftForge.EVENT_BUS.register(this); FMLJavaModLoadingContext.get().getModEventBus().register(this); + recipesPath = Paths.get("config", MOD_ID + ".txt"); + simpleChannel.registerMessage(0, SyncMessage.class, (m, pb) -> { + ListNBT listNBT = new ListNBT(); + m.recipes.forEach(recipe -> listNBT.add(recipe.serializeNBT())); + CompoundNBT tag = new CompoundNBT(); + tag.put("list", listNBT); + pb.writeCompoundTag(tag); + }, pb -> { + SyncMessage m = new SyncMessage(); + ListNBT listNBT = (ListNBT) pb.readCompoundTag().get("list"); + m.recipes = listNBT.stream().map(n -> { + DropRecipe r = new DropRecipe(); + r.deserializeNBT((CompoundNBT) n); + return r; + }).collect(Collectors.toList()); + return m; + }, (m, s) -> { + s.get().enqueueWork(() -> Plugin.recipes = m.recipes); + }); + //eventNetworkChannel. } public static List getAllRecipes(Set allowedIDs, FMLServerStartingEvent event) { + profiler.startTick(); List result = new ArrayList<>(); ServerWorld world = event.getServer().getWorld(DimensionType.OVERWORLD); + Validate.isTrue( + Lists.newArrayList(ForgeRegistries.BLOCKS).size() == Sets.newHashSet(ForgeRegistries.BLOCKS).size()); + Object2LongOpenHashMap cbl = new Object2LongOpenHashMap(); + Object2LongOpenHashMap cbs = new Object2LongOpenHashMap(); for (Block block : ForgeRegistries.BLOCKS) { if (allowedIDs.contains(block.getRegistryName().getNamespace())) { - ImmutableList validStates = block.getStateContainer().getValidStates(); + Stopwatch sw1 = Stopwatch.createStarted(); + List validStates = block.getStateContainer().getValidStates(); + if (validStates.size() > 20) { + validStates = Collections.singletonList(block.getDefaultState()); + } Set dropStrings = new HashSet<>(); + if (block == Blocks.RED_MUSHROOM) { + block.hashCode(); + } for (BlockState state : validStates) { + Stopwatch sw2 = Stopwatch.createStarted(); List drops = getDrops(state, event); String ds = drops.toString(); if (drops.isEmpty() || dropStrings.contains(ds)) { continue; } dropStrings.add(ds); - result.add(new DropRecipe(state.getBlock().getItem(world, BlockPos.ZERO, state), drops)); + result.add(new DropRecipe(getItemForBlock(state, world), drops)); + cbs.addTo(state, sw2.elapsed(TimeUnit.MICROSECONDS)); + System.out.println("Added drop for " + state); } + cbl.addTo(block, sw1.elapsed(TimeUnit.MICROSECONDS)); } } + profiler.endTick(); + System.out.println(profiler.getResults().format()); + System.out.println(StringUtils.repeat("#", 100)); + cbl.object2LongEntrySet().stream().sorted(Comparator.comparingLong(Object2LongMap.Entry::getLongValue)) + .forEach(System.out::println); + cbs.object2LongEntrySet().stream().sorted(Comparator.comparingLong(Object2LongMap.Entry::getLongValue)) + .forEach(System.out::println); return result; } @@ -128,7 +205,12 @@ private static List getDrops(BlockState state, FMLServerStartingEvent even List result = new ArrayList<>(); ServerWorld world = event.getServer().getWorld(DimensionType.OVERWORLD); ServerPlayerEntity player = FakePlayerFactory.get(world, new GameProfile(uuid, "Hacker")); - ItemStack show = state.getBlock().getItem(world, BlockPos.ZERO, state); + ItemStack show; + try { + show = getItemForBlock(state, world); + } catch (RuntimeException e) { + return result; + } if (show.isEmpty()) { return result; } @@ -137,6 +219,7 @@ private static List getDrops(BlockState state, FMLServerStartingEvent even Int2ObjectOpenHashMap> resultMap = new Int2ObjectOpenHashMap<>(); Int2ObjectOpenHashMap>> minmaxs = new Int2ObjectOpenHashMap<>(); for (int fortune = 0; fortune < 4; fortune++) { + //profiler.startSection("a"); ItemStack tool = new ItemStack(Items.DIAMOND_PICKAXE); if (fortune > 0) { tool.addEnchantment(Enchantments.FORTUNE, fortune); @@ -146,6 +229,9 @@ private static List getDrops(BlockState state, FMLServerStartingEvent even tile = state.createTileEntity(world); } catch (Exception e) { } + if (state.getBlock() == Blocks.COAL_ORE && fortune == 3) { + System.out.println(false); + } player.setItemStackToSlot(EquipmentSlotType.MAINHAND, tool); LootContext.Builder builder = new LootContext.Builder(world)// .withParameter(LootParameters.POSITION, BlockPos.ZERO)// @@ -156,67 +242,116 @@ private static List getDrops(BlockState state, FMLServerStartingEvent even Object2IntOpenCustomHashMap stacks = new Object2IntOpenCustomHashMap<>(strategy); Object2ObjectOpenCustomHashMap> minmax = new Object2ObjectOpenCustomHashMap<>( strategy); + minmax.defaultReturnValue(MutablePair.of(9999, 0)); + //profiler.endStartSection("a"); + //profiler.startSection("b"); for (int i = 0; i < iteration; i++) { NonNullList drops = NonNullList.create(); + if (state.getBlock() == Blocks.POTATOES) { + stacks.hashCode(); + } drops.addAll(state.getDrops(builder)); if (!eventCrashed) { try { + /*TODO enable ForgeEventFactory .fireBlockHarvesting(drops, world, BlockPos.ZERO, state, fortune, 1F, false, - player); + player);*/ } catch (Exception e) { eventCrashed = true; } } drops.removeIf(ItemStack::isEmpty); for (ItemStack drop : drops) { - if (all.get() || drop.getItem() != show.getItem()) { + if (all.get() || drop.getItem() != show.getItem() || !(show.getItem() instanceof BlockItem)) { stacks.addTo(drop, drop.getCount()); } - /*minmax.compute(drop, (s, p) -> { - if (p == null) { - return MutablePair.of(drop.getCount(), drop.getCount()); - } - p.setLeft(Math.min(drop.getCount(), p.getLeft())); - p.setRight(Math.max(drop.getCount(), p.getRight())); - return p; - });*/ minmax.merge(drop, MutablePair.of(drop.getCount(), drop.getCount()), (pOld, pNew) -> { pOld.setLeft(Math.min(pNew.getLeft(), pOld.getLeft())); - pOld.setRight(Math.max(pNew.getRight(), pOld.getLeft())); + pOld.setRight(Math.max(pNew.getRight(), pOld.getRight())); return pOld; }); } } minmaxs.put(fortune, minmax); resultMap.put(fortune, stacks); + //profiler.endStartSection("b"); } + //profiler.startSection("c"); ObjectOpenCustomHashSet allStacks = new ObjectOpenCustomHashSet<>(strategy); resultMap.values().forEach(map -> allStacks.addAll(map.keySet())); allStacks.stream()// .sorted(Comparator.comparingInt(s -> Item.getIdFromItem(s.getItem())))// .forEach(s -> { Drop drop = new Drop(s); - for (int i = 0; i < 4; i++) { - drop.getChances().put(i, resultMap.get(i).getInt(s) / (float) iteration); - drop.getMaxs().put(i, minmaxs.get(i).get(s).getRight().intValue()); - drop.getMins().put(i, minmaxs.get(i).get(s).getLeft().intValue()); + if (state.getBlock() == Blocks.OAK_LEAVES) { + state.hashCode(); + } + for (int fortune = 0; fortune < 4; fortune++) { + drop.getChances().put(fortune, resultMap.get(fortune).getInt(s) / (float) iteration); + drop.getMaxs().put(fortune, minmaxs.get(fortune).get(s).getRight().intValue()); + drop.getMins().put(fortune, minmaxs.get(fortune).get(s).getLeft().intValue()); } result.add(drop); }); + //profiler.endStartSection("c"); return result; } catch (Exception e) { + //profiler.endSection(); + LOG.info("Error ({}:{}) while calculating drops for {}", e.getClass().getSimpleName(), e.getMessage(), + state); + if (!true) + throw new RuntimeException(e); return Collections.emptyList(); } } + private static ItemStack getItemForBlock(BlockState state, World world) { + ItemStack item = new ItemStack(state.getBlock().asItem()); + if (item == null) { + item = state.getBlock().getItem(world, BlockPos.ZERO, state); + } + return item; + } + @SubscribeEvent - public void serverStart(FMLServerStartingEvent event) { - if(true)return; - List allRecipes = getAllRecipes( - ModList.get().getMods().stream().map(ModInfo::getModId).filter(s -> !blacklistedMods.get().contains(s)) - .collect(Collectors.toSet()), event); - allRecipes.forEach(System.out::println); + public void serverStart(FMLServerStartingEvent event) throws IOException, CommandSyntaxException { + List allRecipes; + int hash = ModList.get().getMods().stream() + .mapToInt(mi -> mi.getModId().hashCode() ^ mi.getVersion().hashCode()).sum(); + List strings = null; + boolean calculate = Files.notExists(recipesPath); + if (!calculate) { + strings = Files.readAllLines(recipesPath); + String stringHash = strings.get(0); + if (Integer.parseInt(stringHash) != hash) { + calculate = true; + } + } + if (calculate) { + allRecipes = getAllRecipes(ModList.get().getMods().stream().map(ModInfo::getModId) + .filter(s -> !blacklistedMods.get().contains(s)).collect(Collectors.toSet()), event); + List nbts = allRecipes.stream().map(DropRecipe::serializeNBT).map(Object::toString) + .collect(Collectors.toList()); + nbts.add(0, hash + ""); + Files.write(recipesPath, nbts); + } else { + allRecipes = new ArrayList<>(); + for (int i = 1; i < strings.size(); i++) { + DropRecipe dropRecipe = new DropRecipe(); + dropRecipe.deserializeNBT(JsonToNBT.getTagFromJson(strings.get(i))); + allRecipes.add(dropRecipe); + } + } + recipes = allRecipes; + } + + @SubscribeEvent + public void join(EntityJoinWorldEvent event) { + if (event.getEntity() instanceof ServerPlayerEntity) { + simpleChannel.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) event.getEntity()), + new SyncMessage(recipes)); + } } } diff --git a/src/main/java/kdp/blockdrops/Category.java b/src/main/java/kdp/blockdrops/Category.java index 98d7d1c..3294197 100644 --- a/src/main/java/kdp/blockdrops/Category.java +++ b/src/main/java/kdp/blockdrops/Category.java @@ -1,14 +1,23 @@ package kdp.blockdrops; +import java.text.DecimalFormat; +import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.List; +import java.util.Map; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.I18n; import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fml.client.config.GuiButtonExt; import net.minecraftforge.fml.client.config.GuiUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.lwjgl.glfw.GLFW; + +import mezz.jei.Internal; import mezz.jei.api.constants.VanillaTypes; import mezz.jei.api.gui.IRecipeLayout; import mezz.jei.api.gui.drawable.IDrawable; @@ -20,6 +29,8 @@ public class Category implements IRecipeCategory { private static final ResourceLocation hopper = new ResourceLocation("textures/gui/container/hopper.png"); + private static final DecimalFormat format = new DecimalFormat("#.##"); + private final Map> buttonMap = new IdentityHashMap<>(); @Override public ResourceLocation getUid() { @@ -51,6 +62,15 @@ public void draw(DropRecipe recipe, double mouseX, double mouseY) { drawSlot(81, 1); for (int i = 9; i < 170; i += 18) drawSlot(i, 20); + Pair pair = buttonMap.get(recipe); + if (pair == null) { + pair = Pair.of(new Button(0, 23, ""), new Button(172, 23, "")); + buttonMap.put(recipe, pair); + } + pair.getLeft().visible = recipe.getIndex() > 0; + pair.getRight().visible = recipe.getIndex() < recipe.getMaxIndex(); + pair.getLeft().renderButton((int) mouseX, (int) mouseY, 0); + pair.getRight().renderButton((int) mouseX, (int) mouseY, 0); } private void drawSlot(int x, int y) { @@ -69,13 +89,17 @@ public void setRecipe(IRecipeLayout recipeLayout, DropRecipe recipe, IIngredient IGuiItemStackGroup stackGroup = recipeLayout.getItemStacks(); stackGroup.init(0, true, 81, 1); stackGroup.set(0, recipe.getInputs()); + for (int i = 0; i < Math.min(recipe.getOutputs().size(), 9); i++) { + stackGroup.init(i + 1, false, 9 + i * 18, 20); + stackGroup.set(i + 1, recipe.getOutputs().get((i + recipe.getIndex()))); + } stackGroup.addTooltipCallback((int slotIndex, boolean input, ItemStack ingredient, List tooltip) -> { if (!input) { Drop d = recipe.getDropForItem(ingredient); boolean one = d.getChances().values().stream().distinct().count() == 1; for (int x = 0; x < (one ? 1 : 4); x++) { String chance = BlockDrops.showChance.get() ? - String.format("%.2f", d.getChances().get(x) * 100F) + " % " : + format.format(d.getChances().get(x) * 100F) + " % " : ""; String minmax = BlockDrops.showMinMax.get() ? "Min: " + d.getMins().get(x) + " Max: " + d.getMaxs().get(x) : @@ -88,22 +112,51 @@ public void setRecipe(IRecipeLayout recipeLayout, DropRecipe recipe, IIngredient 0) + " ") + TextFormatting.GRAY + chance + minmax); } } - if (recipe.getOutputs().size() > 9) { - tooltip.add( - TextFormatting.RED + "There are too many possible drops. Use left and right key to cycle."); - } } }); - for (int i = 0; i < Math.min(recipe.getOutputs().size(), 9); i++) { - stackGroup.init(i + 1, false, 9 + i * 18, 20); - stackGroup.set(i + 1, recipe.getOutputs().get((i + recipe.getIndex()))); - } } @Override public boolean handleClick(DropRecipe recipe, double mouseX, double mouseY, int mouseButton) { - System.out.println(mouseX + " " + mouseY); - ((IRecipeLogicStateListener) Minecraft.getInstance().currentScreen).onStateChange(); + Pair pair = buttonMap.get(recipe); + if (pair != null// + && (pair.getLeft().isHovered()// + || pair.getRight().isHovered())// + && mouseButton == GLFW.GLFW_MOUSE_BUTTON_1) { + if (pair.getLeft().isHovered()) { + recipe.decreaseIndex(); + } else { + recipe.increaseIndex(); + } + Minecraft.getInstance().enqueue(() ->// + ((IRecipeLogicStateListener) Minecraft.getInstance().currentScreen).onStateChange()); + return true; + } return false; } + + private static class Button extends GuiButtonExt { + + Button(int xPos, int yPos, String displayString) { + super(xPos, yPos, 8, 12, displayString, null); + } + + @Override + public boolean isHovered() { + return super.isHovered() && visible; + } + + @Override + public void renderButton(int mouseX, int mouseY, float partial) { + super.renderButton(mouseX, mouseY, partial); + if (visible) { + boolean left = this.x == 0; + if (left) { + Internal.getTextures().getArrowPrevious().draw(0, 24); + } else { + Internal.getTextures().getArrowNext().draw(171, 24); + } + } + } + } } diff --git a/src/main/java/kdp/blockdrops/ClientEvents.java b/src/main/java/kdp/blockdrops/ClientEvents.java deleted file mode 100644 index 061c25d..0000000 --- a/src/main/java/kdp/blockdrops/ClientEvents.java +++ /dev/null @@ -1,21 +0,0 @@ -package kdp.blockdrops; - -import net.minecraft.client.Minecraft; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.client.event.InputEvent; -import net.minecraftforge.fml.common.Mod; - -import org.lwjgl.glfw.GLFW; - -import mezz.jei.api.runtime.IRecipesGui; - -@Mod.EventBusSubscriber(modid = BlockDrops.MOD_ID, value = Dist.CLIENT) -public class ClientEvents { - - public static void key(InputEvent.KeyInputEvent event) { - if (Minecraft.getInstance().currentScreen instanceof IRecipesGui && event.getAction() == GLFW.GLFW_RELEASE) { - boolean left = event.getKey() == GLFW.GLFW_KEY_LEFT; - boolean right = event.getKey() == GLFW.GLFW_KEY_RIGHT; - } - } -} diff --git a/src/main/java/kdp/blockdrops/Drop.java b/src/main/java/kdp/blockdrops/Drop.java index 6ac07fd..06df292 100644 --- a/src/main/java/kdp/blockdrops/Drop.java +++ b/src/main/java/kdp/blockdrops/Drop.java @@ -6,6 +6,7 @@ import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.IntArrayNBT; import net.minecraftforge.common.util.INBTSerializable; +import net.minecraftforge.items.ItemHandlerHelper; import it.unimi.dsi.fastutil.ints.Int2FloatLinkedOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap; @@ -17,7 +18,7 @@ public class Drop implements INBTSerializable { private final Int2IntLinkedOpenHashMap maxs = new Int2IntLinkedOpenHashMap(); public Drop(ItemStack out) { - this.out = out; + this.out = ItemHandlerHelper.copyStackWithSize(out, 1); } Drop() { diff --git a/src/main/java/kdp/blockdrops/DropRecipe.java b/src/main/java/kdp/blockdrops/DropRecipe.java index f134eb0..662b41a 100644 --- a/src/main/java/kdp/blockdrops/DropRecipe.java +++ b/src/main/java/kdp/blockdrops/DropRecipe.java @@ -26,6 +26,7 @@ public class DropRecipe implements INBTSerializable { public DropRecipe(ItemStack in, List drops) { this.in = in; this.drops = drops; + this.maxIndex = Math.max(0, drops.size() - 9); } public DropRecipe() { @@ -45,6 +46,7 @@ public void setIn(ItemStack in) { public void setDrops(List drops) { this.drops = drops; + this.maxIndex = Math.max(0, drops.size() - 9); } public Drop getDropForItem(ItemStack stack) { @@ -60,6 +62,10 @@ public int getIndex() { return this.index; } + public int getMaxIndex() { + return maxIndex; + } + public void increaseIndex() { this.index = MathHelper.clamp(index + 1, 0, maxIndex); } diff --git a/src/main/java/kdp/blockdrops/Plugin.java b/src/main/java/kdp/blockdrops/Plugin.java index 87854ae..735b5d4 100644 --- a/src/main/java/kdp/blockdrops/Plugin.java +++ b/src/main/java/kdp/blockdrops/Plugin.java @@ -1,11 +1,8 @@ package kdp.blockdrops; -import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.Random; -import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.PickaxeItem; import net.minecraft.util.ResourceLocation; @@ -19,6 +16,9 @@ @JeiPlugin public class Plugin implements IModPlugin { + + static List recipes = null; + @Override public ResourceLocation getPluginUid() { return BlockDrops.RL; @@ -31,27 +31,7 @@ public void registerCategories(IRecipeCategoryRegistration registration) { @Override public void registerRecipes(IRecipeRegistration registration) { - List list = new ArrayList<>(); - Random random = new Random(); - List items = new ArrayList<>(ForgeRegistries.ITEMS.getValues()); - for (int i = 0; i < 8; i++) { - DropRecipe a = new DropRecipe(); - list.add(a); - a.setIn(new ItemStack(items.get(random.nextInt(items.size())))); - List drops = new ArrayList<>(); - a.setDrops(drops); - for (int j = 0; j < random.nextInt(8) + 1; j++) { - Drop d = new Drop(new ItemStack(items.get(random.nextInt(items.size())))); - for (int k = 0; k < 4; k++) { - d.getChances().put(k, random.nextFloat()); - d.getMins().put(k, random.nextInt(5)); - d.getMaxs().put(k, random.nextInt(5)); - } - drops.add(d); - - } - } - registration.addRecipes(list, BlockDrops.RL); + registration.addRecipes(recipes, BlockDrops.RL); } @Override diff --git a/src/main/java/kdp/blockdrops/SyncMessage.java b/src/main/java/kdp/blockdrops/SyncMessage.java new file mode 100644 index 0000000..c53a7c8 --- /dev/null +++ b/src/main/java/kdp/blockdrops/SyncMessage.java @@ -0,0 +1,15 @@ +package kdp.blockdrops; + +import java.util.List; + +public class SyncMessage { + + public List recipes; + + public SyncMessage() { + } + + public SyncMessage(List recipes) { + this.recipes = recipes; + } +} From f9215f38d95dad252d60fdf071cdfd4bd4f73d1c Mon Sep 17 00:00:00 2001 From: KidsDontPlay Date: Sun, 6 Oct 2019 23:24:15 +0200 Subject: [PATCH 4/9] zakk --- src/main/java/kdp/blockdrops/BlockDrops.java | 121 +++++++------------ src/main/java/kdp/blockdrops/Plugin.java | 4 +- 2 files changed, 48 insertions(+), 77 deletions(-) diff --git a/src/main/java/kdp/blockdrops/BlockDrops.java b/src/main/java/kdp/blockdrops/BlockDrops.java index 5825d8d..c0d35f0 100644 --- a/src/main/java/kdp/blockdrops/BlockDrops.java +++ b/src/main/java/kdp/blockdrops/BlockDrops.java @@ -10,20 +10,18 @@ import java.util.Comparator; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import com.google.common.base.Stopwatch; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; import com.mojang.authlib.GameProfile; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.block.Block; import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; import net.minecraft.enchantment.Enchantments; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.inventory.EquipmentSlotType; @@ -34,7 +32,8 @@ import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.JsonToNBT; import net.minecraft.nbt.ListNBT; -import net.minecraft.profiler.Profiler; +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.server.integrated.IntegratedServer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.NonNullList; import net.minecraft.util.ResourceLocation; @@ -47,7 +46,7 @@ import net.minecraftforge.common.ForgeConfigSpec; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.FakePlayerFactory; -import net.minecraftforge.event.entity.EntityJoinWorldEvent; +import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.ModLoadingContext; @@ -58,12 +57,9 @@ import net.minecraftforge.fml.loading.moddiscovery.ModInfo; import net.minecraftforge.fml.network.NetworkRegistry; import net.minecraftforge.fml.network.PacketDistributor; -import net.minecraftforge.fml.network.event.EventNetworkChannel; import net.minecraftforge.fml.network.simple.SimpleChannel; import net.minecraftforge.registries.ForgeRegistries; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.tuple.MutablePair; import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; @@ -72,8 +68,6 @@ import it.unimi.dsi.fastutil.Hash; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap; -import it.unimi.dsi.fastutil.objects.Object2LongMap; -import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; @@ -84,7 +78,7 @@ public class BlockDrops { public static final Logger LOG = LogManager.getLogger(BlockDrops.class); public static final ResourceLocation RL = new ResourceLocation(MOD_ID, "drops"); - public static ForgeConfigSpec.BooleanValue all, showChance, showMinMax, multithreaded; + public static ForgeConfigSpec.BooleanValue all, showChance, showMinMax, multithreaded, allStates; private static ForgeConfigSpec.IntValue iterations; private static ForgeConfigSpec.ConfigValue> blacklistedMods; @@ -107,11 +101,8 @@ public boolean equals(ItemStack a, ItemStack b) { private static final String VERSION = "1.0"; private static SimpleChannel simpleChannel = NetworkRegistry .newSimpleChannel(new ResourceLocation(MOD_ID, "ch1"), () -> VERSION, VERSION::equals, VERSION::equals); - private static EventNetworkChannel eventNetworkChannel = NetworkRegistry - .newEventChannel(new ResourceLocation(MOD_ID, "ev"), () -> VERSION, VERSION::equals, VERSION::equals); private static List recipes = Collections.emptyList(); - private static Profiler profiler = new Profiler(System.nanoTime(), () -> 0); public BlockDrops() { Pair pairCommon = new ForgeConfigSpec.Builder().configure(b -> { @@ -119,10 +110,13 @@ public BlockDrops() { //multithreaded = b.comment("Multithreaded calculation of drops").define("multithreaded", true); iterations = b .comment("Amount of calculation iterations. The higher the more precise the calculation results") - .defineInRange("iterations", 3000, 500, 20000); + .defineInRange("iterations", 4000, 500, 20000); blacklistedMods = b.comment("Mod IDs of mods that won't be scanned").define("blacklistedMods", Arrays.asList("flatcoloredblocks", "chisel", "xtones", "wallpapercraft", "sonarcore", "microblockcbe")); + allStates = b.comment("Only default blockstate of a block is used to calculate the drops", + "Should ordinarily not affect the calculation", "(enable this if you miss some drops)") + .define("allStates", false); return null; }); ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, pairCommon.getValue()); @@ -152,31 +146,25 @@ public BlockDrops() { return m; }, (m, s) -> { s.get().enqueueWork(() -> Plugin.recipes = m.recipes); + s.get().setPacketHandled(true); }); - //eventNetworkChannel. } public static List getAllRecipes(Set allowedIDs, FMLServerStartingEvent event) { - profiler.startTick(); List result = new ArrayList<>(); ServerWorld world = event.getServer().getWorld(DimensionType.OVERWORLD); - Validate.isTrue( - Lists.newArrayList(ForgeRegistries.BLOCKS).size() == Sets.newHashSet(ForgeRegistries.BLOCKS).size()); - Object2LongOpenHashMap cbl = new Object2LongOpenHashMap(); - Object2LongOpenHashMap cbs = new Object2LongOpenHashMap(); + Stopwatch sw = Stopwatch.createStarted(); for (Block block : ForgeRegistries.BLOCKS) { if (allowedIDs.contains(block.getRegistryName().getNamespace())) { - Stopwatch sw1 = Stopwatch.createStarted(); List validStates = block.getStateContainer().getValidStates(); + if (!allStates.get()) { + validStates = Collections.singletonList(validStates.get(validStates.size() - 1)); + } if (validStates.size() > 20) { validStates = Collections.singletonList(block.getDefaultState()); } Set dropStrings = new HashSet<>(); - if (block == Blocks.RED_MUSHROOM) { - block.hashCode(); - } for (BlockState state : validStates) { - Stopwatch sw2 = Stopwatch.createStarted(); List drops = getDrops(state, event); String ds = drops.toString(); if (drops.isEmpty() || dropStrings.contains(ds)) { @@ -184,19 +172,10 @@ public static List getAllRecipes(Set allowedIDs, FMLServerSt } dropStrings.add(ds); result.add(new DropRecipe(getItemForBlock(state, world), drops)); - cbs.addTo(state, sw2.elapsed(TimeUnit.MICROSECONDS)); - System.out.println("Added drop for " + state); } - cbl.addTo(block, sw1.elapsed(TimeUnit.MICROSECONDS)); } } - profiler.endTick(); - System.out.println(profiler.getResults().format()); - System.out.println(StringUtils.repeat("#", 100)); - cbl.object2LongEntrySet().stream().sorted(Comparator.comparingLong(Object2LongMap.Entry::getLongValue)) - .forEach(System.out::println); - cbs.object2LongEntrySet().stream().sorted(Comparator.comparingLong(Object2LongMap.Entry::getLongValue)) - .forEach(System.out::println); + System.out.println(sw.elapsed(TimeUnit.MILLISECONDS) + " millis"); return result; } @@ -219,7 +198,6 @@ private static List getDrops(BlockState state, FMLServerStartingEvent even Int2ObjectOpenHashMap> resultMap = new Int2ObjectOpenHashMap<>(); Int2ObjectOpenHashMap>> minmaxs = new Int2ObjectOpenHashMap<>(); for (int fortune = 0; fortune < 4; fortune++) { - //profiler.startSection("a"); ItemStack tool = new ItemStack(Items.DIAMOND_PICKAXE); if (fortune > 0) { tool.addEnchantment(Enchantments.FORTUNE, fortune); @@ -227,10 +205,7 @@ private static List getDrops(BlockState state, FMLServerStartingEvent even TileEntity tile = null; try { tile = state.createTileEntity(world); - } catch (Exception e) { - } - if (state.getBlock() == Blocks.COAL_ORE && fortune == 3) { - System.out.println(false); + } catch (Exception ignored) { } player.setItemStackToSlot(EquipmentSlotType.MAINHAND, tool); LootContext.Builder builder = new LootContext.Builder(world)// @@ -243,88 +218,78 @@ private static List getDrops(BlockState state, FMLServerStartingEvent even Object2ObjectOpenCustomHashMap> minmax = new Object2ObjectOpenCustomHashMap<>( strategy); minmax.defaultReturnValue(MutablePair.of(9999, 0)); - //profiler.endStartSection("a"); - //profiler.startSection("b"); for (int i = 0; i < iteration; i++) { NonNullList drops = NonNullList.create(); - if (state.getBlock() == Blocks.POTATOES) { - stacks.hashCode(); - } drops.addAll(state.getDrops(builder)); + /*TODO enable if (!eventCrashed) { try { - /*TODO enable ForgeEventFactory .fireBlockHarvesting(drops, world, BlockPos.ZERO, state, fortune, 1F, false, - player);*/ + player); } catch (Exception e) { eventCrashed = true; } - } - drops.removeIf(ItemStack::isEmpty); + }*/ for (ItemStack drop : drops) { + if (drop.isEmpty()) { + continue; + } if (all.get() || drop.getItem() != show.getItem() || !(show.getItem() instanceof BlockItem)) { stacks.addTo(drop, drop.getCount()); + minmax.merge(drop, MutablePair.of(drop.getCount(), drop.getCount()), (pOld, pNew) -> { + pOld.setLeft(Math.min(pNew.getLeft(), pOld.getLeft())); + pOld.setRight(Math.max(pNew.getRight(), pOld.getRight())); + return pOld; + }); } - minmax.merge(drop, MutablePair.of(drop.getCount(), drop.getCount()), (pOld, pNew) -> { - pOld.setLeft(Math.min(pNew.getLeft(), pOld.getLeft())); - pOld.setRight(Math.max(pNew.getRight(), pOld.getRight())); - return pOld; - }); } } minmaxs.put(fortune, minmax); resultMap.put(fortune, stacks); - //profiler.endStartSection("b"); } - //profiler.startSection("c"); ObjectOpenCustomHashSet allStacks = new ObjectOpenCustomHashSet<>(strategy); resultMap.values().forEach(map -> allStacks.addAll(map.keySet())); allStacks.stream()// .sorted(Comparator.comparingInt(s -> Item.getIdFromItem(s.getItem())))// .forEach(s -> { Drop drop = new Drop(s); - if (state.getBlock() == Blocks.OAK_LEAVES) { - state.hashCode(); - } for (int fortune = 0; fortune < 4; fortune++) { drop.getChances().put(fortune, resultMap.get(fortune).getInt(s) / (float) iteration); drop.getMaxs().put(fortune, minmaxs.get(fortune).get(s).getRight().intValue()); - drop.getMins().put(fortune, minmaxs.get(fortune).get(s).getLeft().intValue()); + drop.getMins().put(fortune, drop.getChances().get(fortune) < 1F ? + 0 : + minmaxs.get(fortune).get(s).getLeft().intValue()); } result.add(drop); }); - //profiler.endStartSection("c"); return result; } catch (Exception e) { - //profiler.endSection(); - LOG.info("Error ({}:{}) while calculating drops for {}", e.getClass().getSimpleName(), e.getMessage(), + LOG.info("Error ({} : {}) while calculating drops for {}", e.getClass().getSimpleName(), e.getMessage(), state); - if (!true) - throw new RuntimeException(e); return Collections.emptyList(); } } private static ItemStack getItemForBlock(BlockState state, World world) { - ItemStack item = new ItemStack(state.getBlock().asItem()); - if (item == null) { - item = state.getBlock().getItem(world, BlockPos.ZERO, state); + ItemStack item2 = state.getBlock().getItem(world, BlockPos.ZERO, state); + if (item2.getItem() instanceof BlockItem && ((BlockItem) item2.getItem()).getBlock() == state.getBlock()) { + return item2; } - return item; + return ItemStack.EMPTY; } @SubscribeEvent public void serverStart(FMLServerStartingEvent event) throws IOException, CommandSyntaxException { List allRecipes; - int hash = ModList.get().getMods().stream() - .mapToInt(mi -> mi.getModId().hashCode() ^ mi.getVersion().hashCode()).sum(); + String hash = ModList.get().getMods().stream() + .mapToInt(mi -> mi.getModId().hashCode() ^ mi.getVersion().hashCode()).sum() + ""; List strings = null; boolean calculate = Files.notExists(recipesPath); if (!calculate) { strings = Files.readAllLines(recipesPath); String stringHash = strings.get(0); - if (Integer.parseInt(stringHash) != hash) { + if (!Objects.equals(stringHash, hash)) { calculate = true; } } @@ -333,7 +298,7 @@ public void serverStart(FMLServerStartingEvent event) throws IOException, Comman .filter(s -> !blacklistedMods.get().contains(s)).collect(Collectors.toSet()), event); List nbts = allRecipes.stream().map(DropRecipe::serializeNBT).map(Object::toString) .collect(Collectors.toList()); - nbts.add(0, hash + ""); + nbts.add(0, hash); Files.write(recipesPath, nbts); } else { allRecipes = new ArrayList<>(); @@ -344,11 +309,15 @@ public void serverStart(FMLServerStartingEvent event) throws IOException, Comman } } recipes = allRecipes; + if (event.getServer() instanceof IntegratedServer) { + Plugin.recipes = recipes; + } } @SubscribeEvent - public void join(EntityJoinWorldEvent event) { - if (event.getEntity() instanceof ServerPlayerEntity) { + public void construct(PlayerEvent.PlayerLoggedInEvent event) { + if (event.getEntity() instanceof ServerPlayerEntity && ((ServerPlayerEntity) event + .getEntity()).server instanceof DedicatedServer) { simpleChannel.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) event.getEntity()), new SyncMessage(recipes)); } diff --git a/src/main/java/kdp/blockdrops/Plugin.java b/src/main/java/kdp/blockdrops/Plugin.java index 735b5d4..0aedc62 100644 --- a/src/main/java/kdp/blockdrops/Plugin.java +++ b/src/main/java/kdp/blockdrops/Plugin.java @@ -31,7 +31,9 @@ public void registerCategories(IRecipeCategoryRegistration registration) { @Override public void registerRecipes(IRecipeRegistration registration) { - registration.addRecipes(recipes, BlockDrops.RL); + if (recipes != null) { + registration.addRecipes(recipes, BlockDrops.RL); + } } @Override From 11be5ce11b580f47159c4d5b12cfb91ab42a3b67 Mon Sep 17 00:00:00 2001 From: KidsDontPlay Date: Tue, 8 Oct 2019 21:18:23 +0200 Subject: [PATCH 5/9] build 1.0.0 --- src/main/java/kdp/blockdrops/BlockDrops.java | 8 ++++---- src/main/java/kdp/blockdrops/Category.java | 1 - src/main/resources/META-INF/mods.toml | 6 ++++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/kdp/blockdrops/BlockDrops.java b/src/main/java/kdp/blockdrops/BlockDrops.java index c0d35f0..b871f0c 100644 --- a/src/main/java/kdp/blockdrops/BlockDrops.java +++ b/src/main/java/kdp/blockdrops/BlockDrops.java @@ -78,7 +78,7 @@ public class BlockDrops { public static final Logger LOG = LogManager.getLogger(BlockDrops.class); public static final ResourceLocation RL = new ResourceLocation(MOD_ID, "drops"); - public static ForgeConfigSpec.BooleanValue all, showChance, showMinMax, multithreaded, allStates; + public static ForgeConfigSpec.BooleanValue all, showChance, showMinMax, allStates; private static ForgeConfigSpec.IntValue iterations; private static ForgeConfigSpec.ConfigValue> blacklistedMods; @@ -107,14 +107,13 @@ public boolean equals(ItemStack a, ItemStack b) { public BlockDrops() { Pair pairCommon = new ForgeConfigSpec.Builder().configure(b -> { all = b.comment("Show block drops of any block").define("allBlocks", false); - //multithreaded = b.comment("Multithreaded calculation of drops").define("multithreaded", true); iterations = b .comment("Amount of calculation iterations. The higher the more precise the calculation results") .defineInRange("iterations", 4000, 500, 20000); blacklistedMods = b.comment("Mod IDs of mods that won't be scanned").define("blacklistedMods", Arrays.asList("flatcoloredblocks", "chisel", "xtones", "wallpapercraft", "sonarcore", "microblockcbe")); - allStates = b.comment("Only default blockstate of a block is used to calculate the drops", + allStates = b.comment("Only one blockstate of a block is used to calculate the drops", "Should ordinarily not affect the calculation", "(enable this if you miss some drops)") .define("allStates", false); return null; @@ -154,6 +153,7 @@ public static List getAllRecipes(Set allowedIDs, FMLServerSt List result = new ArrayList<>(); ServerWorld world = event.getServer().getWorld(DimensionType.OVERWORLD); Stopwatch sw = Stopwatch.createStarted(); + LOG.info("Block drop calculation started..."); for (Block block : ForgeRegistries.BLOCKS) { if (allowedIDs.contains(block.getRegistryName().getNamespace())) { List validStates = block.getStateContainer().getValidStates(); @@ -175,7 +175,7 @@ public static List getAllRecipes(Set allowedIDs, FMLServerSt } } } - System.out.println(sw.elapsed(TimeUnit.MILLISECONDS) + " millis"); + LOG.info("Block drop calculation finished after {} milliseconds.", sw.elapsed(TimeUnit.MILLISECONDS)); return result; } diff --git a/src/main/java/kdp/blockdrops/Category.java b/src/main/java/kdp/blockdrops/Category.java index 3294197..aec14d0 100644 --- a/src/main/java/kdp/blockdrops/Category.java +++ b/src/main/java/kdp/blockdrops/Category.java @@ -1,7 +1,6 @@ package kdp.blockdrops; import java.text.DecimalFormat; -import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 378b152..80b0faa 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -25,3 +25,9 @@ description=''' versionRange="[1.14.4,1.15)" ordering="NONE" side="BOTH" +[[dependencies.blockdrops]] + modId="jei" + mandatory=true + versionRange="[6.0.0.18,)" + ordering="NONE" + side="BOTH" \ No newline at end of file From 86bf76064cda6fe12a584163c725a664098587d5 Mon Sep 17 00:00:00 2001 From: KidsDontPlay Date: Wed, 9 Oct 2019 20:02:06 +0200 Subject: [PATCH 6/9] build 1.0.1 -fixed #50 --- build.gradle | 2 +- src/main/java/kdp/blockdrops/BlockDrops.java | 10 ++++------ src/main/resources/META-INF/mods.toml | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 14b14f1..ec520e8 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ apply plugin: 'idea' Properties props = new Properties() props.load(new BufferedReader(new FileReader(file('../gradle.properties')))) -version = "${props.get('mc_version')}-1.0.0" +version = "${props.get('mc_version')}-1.0.1" group = 'kdp.blockdrops' archivesBaseName = 'blockdrops' diff --git a/src/main/java/kdp/blockdrops/BlockDrops.java b/src/main/java/kdp/blockdrops/BlockDrops.java index b871f0c..4e84664 100644 --- a/src/main/java/kdp/blockdrops/BlockDrops.java +++ b/src/main/java/kdp/blockdrops/BlockDrops.java @@ -32,8 +32,6 @@ import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.JsonToNBT; import net.minecraft.nbt.ListNBT; -import net.minecraft.server.dedicated.DedicatedServer; -import net.minecraft.server.integrated.IntegratedServer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.NonNullList; import net.minecraft.util.ResourceLocation; @@ -187,7 +185,7 @@ private static List getDrops(BlockState state, FMLServerStartingEvent even ItemStack show; try { show = getItemForBlock(state, world); - } catch (RuntimeException e) { + } catch (RuntimeException | NoSuchMethodError e) { return result; } if (show.isEmpty()) { @@ -309,15 +307,15 @@ public void serverStart(FMLServerStartingEvent event) throws IOException, Comman } } recipes = allRecipes; - if (event.getServer() instanceof IntegratedServer) { + if (event.getServer().isSinglePlayer()) { Plugin.recipes = recipes; } } @SubscribeEvent public void construct(PlayerEvent.PlayerLoggedInEvent event) { - if (event.getEntity() instanceof ServerPlayerEntity && ((ServerPlayerEntity) event - .getEntity()).server instanceof DedicatedServer) { + if (event.getEntity() instanceof ServerPlayerEntity && !((ServerPlayerEntity) event.getEntity()).server + .isSinglePlayer()) { simpleChannel.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) event.getEntity()), new SyncMessage(recipes)); } diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 80b0faa..134b976 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -4,7 +4,7 @@ issueTrackerURL="http://my.issue.tracker/" [[mods]] modId="blockdrops" -version="1.0.0" +version="1.0.1" displayName="Block Drops" displayURL="https://www.curseforge.com/minecraft/mc-mods/blockdrops" credits="Thanks for this example mod goes to Java" From acd2e3c3fbed3165fcf10cd7ee8de2ec1afd836d Mon Sep 17 00:00:00 2001 From: KidsDontPlay Date: Fri, 11 Oct 2019 20:12:27 +0200 Subject: [PATCH 7/9] build 1.0.2 --- build.gradle | 2 +- src/main/java/kdp/blockdrops/BlockDrops.java | 33 ++++++------ src/main/java/kdp/blockdrops/Tool.java | 54 ++++++++++++++++++++ src/main/resources/META-INF/mods.toml | 2 +- 4 files changed, 72 insertions(+), 19 deletions(-) create mode 100644 src/main/java/kdp/blockdrops/Tool.java diff --git a/build.gradle b/build.gradle index ec520e8..c6e45c4 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ apply plugin: 'idea' Properties props = new Properties() props.load(new BufferedReader(new FileReader(file('../gradle.properties')))) -version = "${props.get('mc_version')}-1.0.1" +version = "${props.get('mc_version')}-1.0.2" group = 'kdp.blockdrops' archivesBaseName = 'blockdrops' diff --git a/src/main/java/kdp/blockdrops/BlockDrops.java b/src/main/java/kdp/blockdrops/BlockDrops.java index 4e84664..862824b 100644 --- a/src/main/java/kdp/blockdrops/BlockDrops.java +++ b/src/main/java/kdp/blockdrops/BlockDrops.java @@ -28,7 +28,6 @@ import net.minecraft.item.BlockItem; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.JsonToNBT; import net.minecraft.nbt.ListNBT; @@ -43,6 +42,7 @@ import net.minecraft.world.storage.loot.LootParameters; import net.minecraftforge.common.ForgeConfigSpec; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.common.util.FakePlayerFactory; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -51,7 +51,6 @@ import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.event.server.FMLServerStartingEvent; -import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.fml.loading.moddiscovery.ModInfo; import net.minecraftforge.fml.network.NetworkRegistry; import net.minecraftforge.fml.network.PacketDistributor; @@ -80,9 +79,9 @@ public class BlockDrops { private static ForgeConfigSpec.IntValue iterations; private static ForgeConfigSpec.ConfigValue> blacklistedMods; - public static UUID uuid = UUID.fromString("1ef41968-f9b8-4350-834e-367f49476a56"); + public static final UUID uuid = UUID.fromString("1ef41968-f9b8-4350-834e-367f49476a56"); - private static Hash.Strategy strategy = new Hash.Strategy() { + private static final Hash.Strategy strategy = new Hash.Strategy() { @Override public int hashCode(ItemStack o) { return o == null || o.isEmpty() ? 0 : o.getItem().hashCode(); @@ -93,11 +92,12 @@ public boolean equals(ItemStack a, ItemStack b) { return a != null && b != null && a.getItem() == b.getItem(); } }; + private static final Tool toolItem = new Tool(); private static Path recipesPath; private static final String VERSION = "1.0"; - private static SimpleChannel simpleChannel = NetworkRegistry + private static final SimpleChannel simpleChannel = NetworkRegistry .newSimpleChannel(new ResourceLocation(MOD_ID, "ch1"), () -> VERSION, VERSION::equals, VERSION::equals); private static List recipes = Collections.emptyList(); @@ -124,7 +124,7 @@ public BlockDrops() { }); ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, pairClient.getValue()); MinecraftForge.EVENT_BUS.register(this); - FMLJavaModLoadingContext.get().getModEventBus().register(this); + recipesPath = Paths.get("config", MOD_ID + ".txt"); simpleChannel.registerMessage(0, SyncMessage.class, (m, pb) -> { ListNBT listNBT = new ListNBT(); @@ -150,6 +150,7 @@ public BlockDrops() { public static List getAllRecipes(Set allowedIDs, FMLServerStartingEvent event) { List result = new ArrayList<>(); ServerWorld world = event.getServer().getWorld(DimensionType.OVERWORLD); + FakePlayer player = FakePlayerFactory.get(world, new GameProfile(uuid, "Hacker")); Stopwatch sw = Stopwatch.createStarted(); LOG.info("Block drop calculation started..."); for (Block block : ForgeRegistries.BLOCKS) { @@ -163,7 +164,7 @@ public static List getAllRecipes(Set allowedIDs, FMLServerSt } Set dropStrings = new HashSet<>(); for (BlockState state : validStates) { - List drops = getDrops(state, event); + List drops = getDrops(state, world, player); String ds = drops.toString(); if (drops.isEmpty() || dropStrings.contains(ds)) { continue; @@ -177,11 +178,9 @@ public static List getAllRecipes(Set allowedIDs, FMLServerSt return result; } - private static List getDrops(BlockState state, FMLServerStartingEvent event) { + private static List getDrops(BlockState state, ServerWorld world, FakePlayer player) { try { List result = new ArrayList<>(); - ServerWorld world = event.getServer().getWorld(DimensionType.OVERWORLD); - ServerPlayerEntity player = FakePlayerFactory.get(world, new GameProfile(uuid, "Hacker")); ItemStack show; try { show = getItemForBlock(state, world); @@ -191,18 +190,19 @@ private static List getDrops(BlockState state, FMLServerStartingEvent even if (show.isEmpty()) { return result; } - boolean eventCrashed = false; int iteration = iterations.get(); Int2ObjectOpenHashMap> resultMap = new Int2ObjectOpenHashMap<>(); Int2ObjectOpenHashMap>> minmaxs = new Int2ObjectOpenHashMap<>(); for (int fortune = 0; fortune < 4; fortune++) { - ItemStack tool = new ItemStack(Items.DIAMOND_PICKAXE); + ItemStack tool = new ItemStack(toolItem); if (fortune > 0) { tool.addEnchantment(Enchantments.FORTUNE, fortune); } TileEntity tile = null; try { tile = state.createTileEntity(world); + tile.setWorld(world); + tile.setPos(BlockPos.ZERO); } catch (Exception ignored) { } player.setItemStackToSlot(EquipmentSlotType.MAINHAND, tool); @@ -216,11 +216,11 @@ private static List getDrops(BlockState state, FMLServerStartingEvent even Object2ObjectOpenCustomHashMap> minmax = new Object2ObjectOpenCustomHashMap<>( strategy); minmax.defaultReturnValue(MutablePair.of(9999, 0)); + boolean eventCrashed = false; for (int i = 0; i < iteration; i++) { NonNullList drops = NonNullList.create(); drops.addAll(state.getDrops(builder)); - /*TODO enable - if (!eventCrashed) { + /*if (!eventCrashed) { try { ForgeEventFactory .fireBlockHarvesting(drops, world, BlockPos.ZERO, state, fortune, 1F, false, @@ -280,8 +280,8 @@ private static ItemStack getItemForBlock(BlockState state, World world) { @SubscribeEvent public void serverStart(FMLServerStartingEvent event) throws IOException, CommandSyntaxException { List allRecipes; - String hash = ModList.get().getMods().stream() - .mapToInt(mi -> mi.getModId().hashCode() ^ mi.getVersion().hashCode()).sum() + ""; + String hash = ModList.get().getMods().stream().map(mi -> "{" + mi.getModId() + ":" + mi.getVersion() + "}") + .sorted().collect(Collectors.joining(",")); List strings = null; boolean calculate = Files.notExists(recipesPath); if (!calculate) { @@ -320,5 +320,4 @@ public void construct(PlayerEvent.PlayerLoggedInEvent event) { new SyncMessage(recipes)); } } - } diff --git a/src/main/java/kdp/blockdrops/Tool.java b/src/main/java/kdp/blockdrops/Tool.java new file mode 100644 index 0000000..c6581c2 --- /dev/null +++ b/src/main/java/kdp/blockdrops/Tool.java @@ -0,0 +1,54 @@ +package kdp.blockdrops; + +import java.util.Collections; + +import net.minecraft.block.BlockState; +import net.minecraft.item.IItemTier; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ToolItem; +import net.minecraft.item.crafting.Ingredient; + +public class Tool extends ToolItem { + private static IItemTier tier = new IItemTier() { + @Override + public int getMaxUses() { + return Integer.MAX_VALUE; + } + + @Override + public float getEfficiency() { + return 100F; + } + + @Override + public float getAttackDamage() { + return Integer.MAX_VALUE; + } + + @Override + public int getHarvestLevel() { + return 10; + } + + @Override + public int getEnchantability() { + return Integer.MAX_VALUE; + } + + @Override + public Ingredient getRepairMaterial() { + return null; + } + }; + + public Tool() { + super(1000F, 1000F, tier, Collections.emptySet(), new Item.Properties()); + setRegistryName("tool"); + } + + @Override + public boolean canHarvestBlock(ItemStack stack, BlockState state) { + return true; + } +} diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 134b976..f1b057e 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -4,7 +4,7 @@ issueTrackerURL="http://my.issue.tracker/" [[mods]] modId="blockdrops" -version="1.0.1" +version="1.0.2" displayName="Block Drops" displayURL="https://www.curseforge.com/minecraft/mc-mods/blockdrops" credits="Thanks for this example mod goes to Java" From 6e40c6b7cb155b3469e9d1fa923708a594bf2265 Mon Sep 17 00:00:00 2001 From: MorningSage <51978143+MorningSage@users.noreply.github.com> Date: Tue, 8 Sep 2020 15:27:13 -0400 Subject: [PATCH 8/9] Hello 1.16.1 --- build.gradle | 4 +- gradle.properties | 7 +- src/main/java/kdp/blockdrops/BlockDrops.java | 47 ++++++------- src/main/java/kdp/blockdrops/Category.java | 69 ++++++++++++-------- src/main/java/kdp/blockdrops/Drop.java | 5 +- src/main/java/kdp/blockdrops/DropRecipe.java | 5 +- src/main/java/kdp/blockdrops/Plugin.java | 11 +++- src/main/java/kdp/blockdrops/Tool.java | 3 +- src/main/resources/META-INF/mods.toml | 12 ++-- 9 files changed, 89 insertions(+), 74 deletions(-) diff --git a/build.gradle b/build.gradle index c6e45c4..5d69c91 100644 --- a/build.gradle +++ b/build.gradle @@ -15,9 +15,9 @@ apply plugin: 'idea' Properties props = new Properties() -props.load(new BufferedReader(new FileReader(file('../gradle.properties')))) +props.load(new BufferedReader(new FileReader(file('gradle.properties')))) -version = "${props.get('mc_version')}-1.0.2" +version = "${props.get('mc_version')}-2.0.2" group = 'kdp.blockdrops' archivesBaseName = 'blockdrops' diff --git a/gradle.properties b/gradle.properties index 878bf1f..e987b12 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,9 @@ # Sets default memory used for gradle commands. Can be overridden by user or command line properties. # This is required to provide enough memory for the Minecraft decompilation process. org.gradle.jvmargs=-Xmx3G -org.gradle.daemon=false \ No newline at end of file +org.gradle.daemon=false + +mapping=20200723-1.16.1 +mc_version=1.16.1 +forge_version=32.0.108 +jei_version=7.0.1.10 \ No newline at end of file diff --git a/src/main/java/kdp/blockdrops/BlockDrops.java b/src/main/java/kdp/blockdrops/BlockDrops.java index 862824b..a62da9e 100644 --- a/src/main/java/kdp/blockdrops/BlockDrops.java +++ b/src/main/java/kdp/blockdrops/BlockDrops.java @@ -28,6 +28,8 @@ import net.minecraft.item.BlockItem; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.loot.LootContext; +import net.minecraft.loot.LootParameters; import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.JsonToNBT; import net.minecraft.nbt.ListNBT; @@ -36,10 +38,7 @@ import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; -import net.minecraft.world.dimension.DimensionType; import net.minecraft.world.server.ServerWorld; -import net.minecraft.world.storage.loot.LootContext; -import net.minecraft.world.storage.loot.LootParameters; import net.minecraftforge.common.ForgeConfigSpec; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.FakePlayer; @@ -134,12 +133,17 @@ public BlockDrops() { pb.writeCompoundTag(tag); }, pb -> { SyncMessage m = new SyncMessage(); - ListNBT listNBT = (ListNBT) pb.readCompoundTag().get("list"); - m.recipes = listNBT.stream().map(n -> { - DropRecipe r = new DropRecipe(); - r.deserializeNBT((CompoundNBT) n); - return r; - }).collect(Collectors.toList()); + CompoundNBT nbt = pb.readCompoundTag(); + if (nbt != null) { + ListNBT listNBT = (ListNBT) nbt.get("list"); + if (listNBT != null) { + m.recipes = listNBT.stream().map(n -> { + DropRecipe r = new DropRecipe(); + r.deserializeNBT((CompoundNBT) n); + return r; + }).collect(Collectors.toList()); + } + } return m; }, (m, s) -> { s.get().enqueueWork(() -> Plugin.recipes = m.recipes); @@ -149,12 +153,12 @@ public BlockDrops() { public static List getAllRecipes(Set allowedIDs, FMLServerStartingEvent event) { List result = new ArrayList<>(); - ServerWorld world = event.getServer().getWorld(DimensionType.OVERWORLD); + ServerWorld world = event.getServer().getWorld(World.field_234918_g_); FakePlayer player = FakePlayerFactory.get(world, new GameProfile(uuid, "Hacker")); Stopwatch sw = Stopwatch.createStarted(); LOG.info("Block drop calculation started..."); for (Block block : ForgeRegistries.BLOCKS) { - if (allowedIDs.contains(block.getRegistryName().getNamespace())) { + if (block.getRegistryName() != null && allowedIDs.contains(block.getRegistryName().getNamespace())) { List validStates = block.getStateContainer().getValidStates(); if (!allStates.get()) { validStates = Collections.singletonList(validStates.get(validStates.size() - 1)); @@ -201,8 +205,7 @@ private static List getDrops(BlockState state, ServerWorld world, FakePlay TileEntity tile = null; try { tile = state.createTileEntity(world); - tile.setWorld(world); - tile.setPos(BlockPos.ZERO); + if (tile != null) tile.setWorldAndPos(world, BlockPos.ZERO); } catch (Exception ignored) { } player.setItemStackToSlot(EquipmentSlotType.MAINHAND, tool); @@ -216,23 +219,11 @@ private static List getDrops(BlockState state, ServerWorld world, FakePlay Object2ObjectOpenCustomHashMap> minmax = new Object2ObjectOpenCustomHashMap<>( strategy); minmax.defaultReturnValue(MutablePair.of(9999, 0)); - boolean eventCrashed = false; for (int i = 0; i < iteration; i++) { NonNullList drops = NonNullList.create(); drops.addAll(state.getDrops(builder)); - /*if (!eventCrashed) { - try { - ForgeEventFactory - .fireBlockHarvesting(drops, world, BlockPos.ZERO, state, fortune, 1F, false, - player); - } catch (Exception e) { - eventCrashed = true; - } - }*/ for (ItemStack drop : drops) { - if (drop.isEmpty()) { - continue; - } + if (drop.isEmpty()) continue; if (all.get() || drop.getItem() != show.getItem() || !(show.getItem() instanceof BlockItem)) { stacks.addTo(drop, drop.getCount()); minmax.merge(drop, MutablePair.of(drop.getCount(), drop.getCount()), (pOld, pNew) -> { @@ -256,8 +247,7 @@ private static List getDrops(BlockState state, ServerWorld world, FakePlay drop.getChances().put(fortune, resultMap.get(fortune).getInt(s) / (float) iteration); drop.getMaxs().put(fortune, minmaxs.get(fortune).get(s).getRight().intValue()); drop.getMins().put(fortune, drop.getChances().get(fortune) < 1F ? - 0 : - minmaxs.get(fortune).get(s).getLeft().intValue()); + 0 : minmaxs.get(fortune).get(s).getLeft()); } result.add(drop); }); @@ -270,6 +260,7 @@ private static List getDrops(BlockState state, ServerWorld world, FakePlay } private static ItemStack getItemForBlock(BlockState state, World world) { + @SuppressWarnings("deprecation") ItemStack item2 = state.getBlock().getItem(world, BlockPos.ZERO, state); if (item2.getItem() instanceof BlockItem && ((BlockItem) item2.getItem()).getBlock() == state.getBlock()) { return item2; diff --git a/src/main/java/kdp/blockdrops/Category.java b/src/main/java/kdp/blockdrops/Category.java index aec14d0..b7f724d 100644 --- a/src/main/java/kdp/blockdrops/Category.java +++ b/src/main/java/kdp/blockdrops/Category.java @@ -5,13 +5,16 @@ import java.util.List; import java.util.Map; +import com.mojang.blaze3d.matrix.MatrixStack; +import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.AbstractGui; import net.minecraft.client.resources.I18n; import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.StringTextComponent; import net.minecraft.util.text.TextFormatting; -import net.minecraftforge.fml.client.config.GuiButtonExt; -import net.minecraftforge.fml.client.config.GuiUtils; import org.apache.commons.lang3.tuple.Pair; import org.lwjgl.glfw.GLFW; @@ -26,6 +29,10 @@ import mezz.jei.gui.elements.DrawableBlank; import mezz.jei.gui.recipes.IRecipeLogicStateListener; +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault public class Category implements IRecipeCategory { private static final ResourceLocation hopper = new ResourceLocation("textures/gui/container/hopper.png"); private static final DecimalFormat format = new DecimalFormat("#.##"); @@ -53,28 +60,30 @@ public IDrawable getBackground() { @Override public IDrawable getIcon() { + //noinspection ConstantConditions return null; } + + @Override - public void draw(DropRecipe recipe, double mouseX, double mouseY) { - drawSlot(81, 1); - for (int i = 9; i < 170; i += 18) - drawSlot(i, 20); + public void draw(DropRecipe recipe, MatrixStack matrixStack, double mouseX, double mouseY) { + drawSlot(matrixStack, 81, 1); + for (int i = 9; i < 170; i += 18) drawSlot(matrixStack, i, 20); Pair pair = buttonMap.get(recipe); if (pair == null) { pair = Pair.of(new Button(0, 23, ""), new Button(172, 23, "")); buttonMap.put(recipe, pair); } - pair.getLeft().visible = recipe.getIndex() > 0; - pair.getRight().visible = recipe.getIndex() < recipe.getMaxIndex(); - pair.getLeft().renderButton((int) mouseX, (int) mouseY, 0); - pair.getRight().renderButton((int) mouseX, (int) mouseY, 0); + //pair.getLeft().visible = recipe.getIndex() > 0; + //pair.getRight().visible = recipe.getIndex() < recipe.getMaxIndex(); + if (recipe.getIndex() > 0) pair.getLeft().renderButton(matrixStack, (int) mouseX, (int) mouseY, 0); + if (recipe.getIndex() < recipe.getMaxIndex()) pair.getRight().renderButton(matrixStack, (int) mouseX, (int) mouseY, 0); } - private void drawSlot(int x, int y) { + private void drawSlot(MatrixStack matrixStack, int x, int y) { Minecraft.getInstance().getTextureManager().bindTexture(hopper); - GuiUtils.drawTexturedModalRect(x, y, 43, 19, 18, 18, 0); + AbstractGui.blit(matrixStack, x, y, 43, 19, 18, 18, 256, 256); } @Override @@ -92,7 +101,7 @@ public void setRecipe(IRecipeLayout recipeLayout, DropRecipe recipe, IIngredient stackGroup.init(i + 1, false, 9 + i * 18, 20); stackGroup.set(i + 1, recipe.getOutputs().get((i + recipe.getIndex()))); } - stackGroup.addTooltipCallback((int slotIndex, boolean input, ItemStack ingredient, List tooltip) -> { + stackGroup.addTooltipCallback((int slotIndex, boolean input, ItemStack ingredient, List tooltip) -> { if (!input) { Drop d = recipe.getDropForItem(ingredient); boolean one = d.getChances().values().stream().distinct().count() == 1; @@ -104,11 +113,9 @@ public void setRecipe(IRecipeLayout recipeLayout, DropRecipe recipe, IIngredient "Min: " + d.getMins().get(x) + " Max: " + d.getMaxs().get(x) : ""; if (!chance.isEmpty() || !minmax.isEmpty()) { - tooltip.add((one ? - "" : - TextFormatting.BLUE + "Fortune " + (0 != x ? - I18n.format("enchantment.level." + x) : - 0) + " ") + TextFormatting.GRAY + chance + minmax); + String text = ""; + if (!one) text = TextFormatting.BLUE + "Fortune " + (0 != x ? I18n.format("enchantment.level." + x) : 0) + " "; + tooltip.add(new StringTextComponent(text + TextFormatting.GRAY + chance + minmax)); } } } @@ -118,26 +125,30 @@ public void setRecipe(IRecipeLayout recipeLayout, DropRecipe recipe, IIngredient @Override public boolean handleClick(DropRecipe recipe, double mouseX, double mouseY, int mouseButton) { Pair pair = buttonMap.get(recipe); - if (pair != null// - && (pair.getLeft().isHovered()// - || pair.getRight().isHovered())// + if (pair != null + && (pair.getLeft().isHovered() + || pair.getRight().isHovered()) && mouseButton == GLFW.GLFW_MOUSE_BUTTON_1) { if (pair.getLeft().isHovered()) { recipe.decreaseIndex(); } else { recipe.increaseIndex(); } - Minecraft.getInstance().enqueue(() ->// - ((IRecipeLogicStateListener) Minecraft.getInstance().currentScreen).onStateChange()); + Minecraft.getInstance().enqueue(() -> { + if (Minecraft.getInstance().currentScreen != null) { + ((IRecipeLogicStateListener) Minecraft.getInstance().currentScreen).onStateChange(); + } + }); + return true; } return false; } - private static class Button extends GuiButtonExt { + private static class Button extends net.minecraft.client.gui.widget.button.Button { Button(int xPos, int yPos, String displayString) { - super(xPos, yPos, 8, 12, displayString, null); + super(xPos, yPos, 8, 12, new StringTextComponent(displayString), button -> { }); } @Override @@ -146,14 +157,14 @@ public boolean isHovered() { } @Override - public void renderButton(int mouseX, int mouseY, float partial) { - super.renderButton(mouseX, mouseY, partial); + public void renderButton(MatrixStack matrixStack, int mouseX, int mouseY, float partial) { + super.renderButton(matrixStack, mouseX, mouseY, partial); if (visible) { boolean left = this.x == 0; if (left) { - Internal.getTextures().getArrowPrevious().draw(0, 24); + Internal.getTextures().getArrowPrevious().draw(matrixStack, 0, 24); } else { - Internal.getTextures().getArrowNext().draw(171, 24); + Internal.getTextures().getArrowNext().draw(matrixStack, 171, 24); } } } diff --git a/src/main/java/kdp/blockdrops/Drop.java b/src/main/java/kdp/blockdrops/Drop.java index 06df292..49b3028 100644 --- a/src/main/java/kdp/blockdrops/Drop.java +++ b/src/main/java/kdp/blockdrops/Drop.java @@ -5,6 +5,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.IntArrayNBT; +import net.minecraft.util.ResourceLocation; import net.minecraftforge.common.util.INBTSerializable; import net.minecraftforge.items.ItemHandlerHelper; @@ -16,6 +17,7 @@ public class Drop implements INBTSerializable { private final Int2FloatLinkedOpenHashMap chances = new Int2FloatLinkedOpenHashMap(); private final Int2IntLinkedOpenHashMap mins = new Int2IntLinkedOpenHashMap(); private final Int2IntLinkedOpenHashMap maxs = new Int2IntLinkedOpenHashMap(); + public static final Drop EMPTY = new Drop(); public Drop(ItemStack out) { this.out = ItemHandlerHelper.copyStackWithSize(out, 1); @@ -42,7 +44,8 @@ public Int2IntLinkedOpenHashMap getMaxs() { @Override public String toString() { - return out.getItem().getRegistryName().toString(); + ResourceLocation name = out.getItem().getRegistryName(); + return name == null ? "" : name.toString(); } @Override diff --git a/src/main/java/kdp/blockdrops/DropRecipe.java b/src/main/java/kdp/blockdrops/DropRecipe.java index 662b41a..1c37b4d 100644 --- a/src/main/java/kdp/blockdrops/DropRecipe.java +++ b/src/main/java/kdp/blockdrops/DropRecipe.java @@ -19,7 +19,7 @@ public class DropRecipe implements INBTSerializable { private ItemStack in; private List drops; - private Cache cache = CacheBuilder.newBuilder().build(); + private final Cache cache = CacheBuilder.newBuilder().build(); //client only private int index, maxIndex; @@ -51,8 +51,7 @@ public void setDrops(List drops) { public Drop getDropForItem(ItemStack stack) { try { - return cache.get(stack, - () -> drops.stream().filter(drop -> drop.getOut().isItemEqual(stack)).findFirst().get()); + return cache.get(stack, () -> drops.stream().filter(drop -> drop.getOut().isItemEqual(stack)).findFirst().orElse(Drop.EMPTY)); } catch (ExecutionException e) { throw new RuntimeException(e); } diff --git a/src/main/java/kdp/blockdrops/Plugin.java b/src/main/java/kdp/blockdrops/Plugin.java index 0aedc62..3d92b17 100644 --- a/src/main/java/kdp/blockdrops/Plugin.java +++ b/src/main/java/kdp/blockdrops/Plugin.java @@ -3,6 +3,7 @@ import java.util.Comparator; import java.util.List; +import mcp.MethodsReturnNonnullByDefault; import net.minecraft.item.ItemStack; import net.minecraft.item.PickaxeItem; import net.minecraft.util.ResourceLocation; @@ -14,7 +15,11 @@ import mezz.jei.api.registration.IRecipeCategoryRegistration; import mezz.jei.api.registration.IRecipeRegistration; +import javax.annotation.ParametersAreNonnullByDefault; + @JeiPlugin +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault public class Plugin implements IModPlugin { static List recipes = null; @@ -38,9 +43,9 @@ public void registerRecipes(IRecipeRegistration registration) { @Override public void registerRecipeCatalysts(IRecipeCatalystRegistration registration) { - ForgeRegistries.ITEMS.getValues().stream()// - .filter(i -> i instanceof PickaxeItem && i.getRegistryName().getNamespace().equals("minecraft"))// - .sorted(Comparator.comparingInt(i -> ((PickaxeItem) i).getTier().getMaxUses()).reversed())// + ForgeRegistries.ITEMS.getValues().stream() + .filter(i -> i instanceof PickaxeItem && i.getRegistryName() != null && i.getRegistryName().getNamespace().equals("minecraft")) + .sorted(Comparator.comparingInt(i -> ((PickaxeItem) i).getTier().getMaxUses()).reversed()) .forEach(i -> registration.addRecipeCatalyst(new ItemStack(i), BlockDrops.RL)); } } diff --git a/src/main/java/kdp/blockdrops/Tool.java b/src/main/java/kdp/blockdrops/Tool.java index c6581c2..dd13f3b 100644 --- a/src/main/java/kdp/blockdrops/Tool.java +++ b/src/main/java/kdp/blockdrops/Tool.java @@ -10,7 +10,7 @@ import net.minecraft.item.crafting.Ingredient; public class Tool extends ToolItem { - private static IItemTier tier = new IItemTier() { + private static final IItemTier tier = new IItemTier() { @Override public int getMaxUses() { return Integer.MAX_VALUE; @@ -37,6 +37,7 @@ public int getEnchantability() { } @Override + @SuppressWarnings("NullableProblems") public Ingredient getRepairMaterial() { return null; } diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index f1b057e..9a96083 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -1,14 +1,14 @@ modLoader="javafml" -loaderVersion="[28,)" +loaderVersion="[32,)" issueTrackerURL="http://my.issue.tracker/" [[mods]] modId="blockdrops" -version="1.0.2" +version="2.0.2" displayName="Block Drops" displayURL="https://www.curseforge.com/minecraft/mc-mods/blockdrops" credits="Thanks for this example mod goes to Java" -authors="KidsDontPlay" +authors="KidsDontPlay, MorningSage" description=''' ''' @@ -16,18 +16,18 @@ description=''' [[dependencies.blockdrops]] modId="forge" mandatory=true - versionRange="[28,)" + versionRange="[32,)" ordering="NONE" side="BOTH" [[dependencies.blockdrops]] modId="minecraft" mandatory=true - versionRange="[1.14.4,1.15)" + versionRange="[1.16,)" ordering="NONE" side="BOTH" [[dependencies.blockdrops]] modId="jei" mandatory=true - versionRange="[6.0.0.18,)" + versionRange="[7.0.1.10,)" ordering="NONE" side="BOTH" \ No newline at end of file From 6d12d3a3b9f89494f48439513d6dbeb0df6c5fa6 Mon Sep 17 00:00:00 2001 From: MorningSage <51978143+MorningSage@users.noreply.github.com> Date: Tue, 8 Sep 2020 16:01:53 -0400 Subject: [PATCH 9/9] Hello 1.16.1 --- src/main/java/kdp/blockdrops/BlockDrops.java | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/java/kdp/blockdrops/BlockDrops.java b/src/main/java/kdp/blockdrops/BlockDrops.java index a62da9e..158926a 100644 --- a/src/main/java/kdp/blockdrops/BlockDrops.java +++ b/src/main/java/kdp/blockdrops/BlockDrops.java @@ -240,17 +240,17 @@ private static List getDrops(BlockState state, ServerWorld world, FakePlay ObjectOpenCustomHashSet allStacks = new ObjectOpenCustomHashSet<>(strategy); resultMap.values().forEach(map -> allStacks.addAll(map.keySet())); allStacks.stream()// - .sorted(Comparator.comparingInt(s -> Item.getIdFromItem(s.getItem())))// - .forEach(s -> { - Drop drop = new Drop(s); - for (int fortune = 0; fortune < 4; fortune++) { - drop.getChances().put(fortune, resultMap.get(fortune).getInt(s) / (float) iteration); - drop.getMaxs().put(fortune, minmaxs.get(fortune).get(s).getRight().intValue()); - drop.getMins().put(fortune, drop.getChances().get(fortune) < 1F ? - 0 : minmaxs.get(fortune).get(s).getLeft()); - } - result.add(drop); - }); + .sorted(Comparator.comparingInt(s -> Item.getIdFromItem(s.getItem())))// + .forEach(s -> { + Drop drop = new Drop(s); + for (int fortune = 0; fortune < 4; fortune++) { + drop.getChances().put(fortune, resultMap.get(fortune).getInt(s) / (float) iteration); + drop.getMaxs().put(fortune, minmaxs.get(fortune).get(s).getRight().intValue()); + drop.getMins().put(fortune, drop.getChances().get(fortune) < 1F ? + 0 : minmaxs.get(fortune).get(s).getLeft().intValue()); + } + result.add(drop); + }); return result; } catch (Exception e) { LOG.info("Error ({} : {}) while calculating drops for {}", e.getClass().getSimpleName(), e.getMessage(),